💒前言
??如是初学者,建议按照以下顺序学习 1、C++入门基础
??学习本章 需要掌握C语言 中的结构体
🧸面向过程 和面向对象
??对于C 语言来说是 面向过程的语言 ??C++ 是面向对象+面向过程,因为C++ 兼容C ??java 是面向对象的语言。 定义 举例:今晚吃 西红柿鸡蛋盖饭 ??面向过程:需要自己做,1.切西红柿备用。2.起锅烧油。3.放入鸡蛋炒熟备用… ??面向对象:点了个外卖(西红柿炒鸡蛋盖饭)
💒类和对象(上)
🧸类:
??先看代码 ,后面会解释
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
在C中有 struct 定义的结构体
在C++ 中兼容C 的结果构体,但是已经升级为类
并且默认访问 是public(公有)
而且在结构体中可以定义成员函数 并且调用
struct Stucent
{
char name[10];
int age;
int id;
};
int main()
{
struct Stucent s1;
strcpy(s1._name, "李四");
s1.id = 1;
s1.age = 18;
printf("%s\n%d\n%d\n",s1.name,s1.age,s1.id);
}
在C++中用class 定义类
class Stucent
{
public:
char _name[10];
int _age;
int _id;
void Init(const char* name, int age, int id)
{
strcpy(_name, name);
_age = age;
_id = id;
}
private:
void Print()
{
cout << _name << endl;
cout << _age << endl;
cout << _id << endl;
}
};
int main()
{
Stucent s2;
strcpy(s2._name, "李四");
s2._id = 1;
s2._age = 18;
s2.Init("张三",18, 1);
s2.Print();
}
??在C++ 中用class 定义类,类中包含 成员变量、成员函数; ??class 中定义的类 是有访问权限的,默认是私有的,外部函数不可以访问
面向对象的三大特性:封装、继承、多态。
??默认 访问限定符是private (私有) ??访问限定符: ????public 公有 ????protected 保护 ????private 私有
访问限定符的作用域 ??访问限定符的作用域 遇到下一个限定符结束 或者类的底部结束
类的实例化 ??用类名定义的 变量 - 称之为 类实例化的对象
C++类中的成员对象 也就是C结构体中的变量 类对象的大小 ??类中有两种对象 - 成员变量 、成员函数 ??存储方式 是 实例化对象字存储成员变量、成员函数放在公共代码区,因为 成员函数 是相同的 没必要多处拷贝
class A1 {
public:
void f1(){}
private:
int _a;
};
class A2 {
public:
void f2() {}
};
class A3
{};
sizeof(A1) : __4___ sizeof(A2) : __1___ sizeof(A3) : ___1__
类的大小也需要空间对齐,规则与结构体相同 类无变量时 用一个字符标识这个类,存了一个地址
💒类与对象(中)
??六个默认成员函数:构造函数、析构函数、拷贝构造函数、赋值运算符重载
🧸构造函数
??在实例化对象时初始化用的函数,我们自己写了,编译器会调用我们写的,我们不写,编译器会自动生成。对于内置类型成员变量不处理,对于自定义类型成员变量调用它的默认构造函数。
class Date
{
构造函数 要与定义的类名一致
1.Date() {}; 不能传参
2.Date(int a,int b){};必须传参
3.Date(int a = 0,int b = 0){}; 不传参用缺省值,传参用形参
4.Date(capacaty)
{
Stack* ret = (Stack*)malloc(sizeof(Stack)*capacaty)
_capacaty = capacaty;
}
};
🧸析构函数
??用于清理对象中的资源,如果独享需要资源,才需要自己实现析构函数,析构函数在函数生命周期到了,自动调用。内置类型不处理,自定义类型调用它的析构函数。
1.对于malloc的对象需要自己定义析构函数
class Stack
{
int*a;
int _val;
};
2.对于日期类对象不需要自己定义析构函数
class Date
{
int _year;
int _month;
};
🧸拷贝构造函数
??使用同类型的对象去初始化对象,我们不写,编译器自动生成。 ??自动生成 ????1.内置类型完成按字节序的拷贝 - 浅拷贝 对于指针的拷贝会把指针的地址拷贝过去,造成新初始化的对象与原对象指向同一块空间。 ????2.自定义类型的成员变量,调用它的拷贝构造。
1.对于日期类,可以不写,浅拷贝够用
Date(const Date& d) 需要注意这里传引用,不然会死循环调用拷贝构造
{
_year = d._year;
_month = d._month;
_day = d._day;
}
2.对于栈类,需要拷贝构造,因为有指针
3.对于以下,不需要,因为会调用它的拷贝构造
class MyQueue
{
Stack _pushST;
Stack _popST;
}
🧸运算符重载
??关键字:返回值类型operator 操作符(参数列表) ??自定义类型不支持运算符使用,所以需要自己写 ??不能被重载的运算符(5个):.* 、:: 、sizeof 、?: 、.
大于操作符,用引用是为了防止原始数据被改,并可以减少拷贝构造的调用
class Date
{
public:
bool Date::operator>(const Date& d)
{
if(_year>d._year)
return true;
else if(_year==d._year && _month>d._month)
return true;
else if(_year==d._year && _month==d._month && _day>d._day)
return true;
else
return false;
}
private:
int _year;
int _month;
int _day;
}
调用:
int mian()
{
d1 > d2;
}
说明:、
如果operator 与main在一个.cpp中 则可以 operator>(d1,d2);这样调用
如果:1.operator>在类中,则字默认增加隐藏参数 this
2.所以要减少一个参数,this 就是d1
运算符重载 调用顺序,先去成员函数找,再去全局找
🧸赋值重载
?? 两个已经存在的对象之间 赋值
Date d1(2022,4,17);
Date d3(2022,5,23);
对刚创建的值进行初始化 - 拷贝构造
Date d4(d1);
Date d5 = d1;
两个已经存在的对象之间 赋值 - 赋值拷贝
d1 = d3;
赋值运算符的返回值应该是d1
int i,j,k;
j = i = k = 10;
赋值运算符的实现
Date& operator=(const Date& d)
{
优化:自己个自己赋值 判断一下
if(this != &d)
{
_year = d.year;
_month = d.month;
_day = d.day;
}
return *this;
}
因为 函数调用结束,d1还存在,所以用引用返回,可以减少拷贝构造
梳理
赋值重载、拷贝构造、构造函数、析构函数分别是干嘛用的? ??1.赋值重载 :给两个已经存在的对象进行复制,在这期间会产生临时拷贝,所以会调用拷贝构造,为了减少拷贝构造,参数用了引用参数,因为赋值运算要有返回值作为右值,所以需要返回this参数,,为了减少拷贝构造,返回参数用了引用返回。 ??2.拷贝构造 :用来对刚创建的对象进行赋值,也用于函数传参,临时拷贝用 ??3.构造函数 :用于初始化新建的类,一般会用缺省参数,便于创建对象时的赋值。 ??4.析构函数 :对对象的清理
赋值重载和拷贝构造 代码基本一致 构造函数和析构函数 代码基本一致 以上函数都是 编译器自动调用,不写时自动产生。
- += 与运算符重载
??直接将天数加到 day上 当day小于这个月应该有的天数则结束 返回,如果大于当月天数,则day-当月天数 ,然后进位月份,月份++,当月份>12时 重置为1,年++; ??+=是给自身加 比如i=5;i+=10; 则i = 15;
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
}
- 日期+天数
??天数相加 ,+运算符不会改变自身,例如:i= 5; ret = i+10; 最后i还是5 ret是15;,所以这里为了不改变this 先用ret代替*this,在改变ret 返回ret ,又因为ret出函数销毁,所以不能用引用返回,用临时变量(调用拷贝构造,拷贝临时变量)返回
Date Date::operator+(int day)
{
Date ret = *this;
ret += day;
return ret;
}
- 前置++
??前置++ 直接加就可以了 返回原来的值,因为原来的值出作用域补销毁,所以用引用返回。 ??前置++ 不会进行拷贝 ,建议用前置++
Date& Date::operator++()
{
*this += 1;
return *this;
}
- 后置++
??后置++ 为了与前置++区分 多给一个参数int 传什么值不重要
Date Date::operator++(int)
{
Date ret = (*this);
*this + 1;
return ret;
}
**日期类 请单独查看** 析构 在同一个生命周期里 先创建的后析构
🧸const成员
const 修饰指针
const 放* 左面 修饰*p不能改
const 放* 右面 修饰p 不能改
void Display() const
{
}
void Display(const Date* this)
{
}
用来修饰 this指针 意思是指针指向的内容不能被修改
🧸取地址及const取地址操作符重载
??这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ;
int _month ;
int _day ;
};
💒类与对象(下)
🧸初始化列表
class B
{
public:
B(int a, int ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{}
private:
A _aobj;
int& _ref;
const int _n;
};
对于引用成员、const成员、自定义类型成员,需要在初始化列表初始化
static成员 初始化,静态变量一定在类外初始化
使用初始化列表可以更高效
Date(const A& aa)
:_aa(aa)
{}
int main()
{
A aa(10);
}
上述调用一次拷贝构造 和一次构造函数
Date(const A& aa)
{
:_aa(aa)
}
int main()
{
A aa(10);
}
上述调用一次拷贝构造 和两次构造函数
因为初始化列表是成员变量定义的地方,所以_aa会在初始化列表定义的调用默认构造函数初始化
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)
class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
class Solution {
public:
int Sum_Solution(int n) {
Sum arr[n];
return Sum::GetRet();
}
};
结构体数组 定义的时候n是多少就会调用多少次默认构造函数
🧸C++11 的成员初始化新玩法
private:
private:
int _a1 = 0;
B _bb;
B _bb2 = 10;
B _bb3 = B(10);
int* p = (int*)malloc(4 * 10);
int arr[10] = { 1,2,3,4,5,6,7 };
};
这里不是初始化 是缺省值
传参用传参
构造函数有缺省值用构造函数缺省值
最后用成员声明处的缺省值
🧸友元函数
关键字friend 外部函数 在类内使用友元函数则可以访问类内的私有成员变量
friend ostream& operator<<(ostream& _cout, const Date& d);
🧸友元类
Date可以访问Time类
class Time
{
friend class Date;
}
🧸内部类
??1. 内部类可以定义在外部类的public、protected、private都是可以的。 ??2. 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。 ??3. sizeof(外部类)=外部类,和内部类没有任何关系。 A不能访问B成员 B能访问A
class A
{
private:
static int k;
int h;
public:
class B
{
public:
void foo(const A& a)
{
cout << k << endl;
cout << a.h << endl;
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
💒类和对象完💒
|