类的定义
class为定义类的关键字,ClassName为类的名字,{}; 中为类的主体
类中的元素称为类的成员,类中的数据称为成员变量或属性;类中的函数称为成员函数或者类的 方法
-
类的两种定义方式
- 声明和定义全部放在类体中 注意: 成员函数在类中定义, 默认内联
- 声明放在.h文件中, 类的定义放在.cpp文件
-
类的访问限定符及封装
- public修饰的成员在类外直接被访问
- protectd和private修饰的成员在类外不能直接访问
- 访问限定符作用域从该访问限定符出现的位置直到下一个访问限定符
- class中如果不写访问限定符默认为private,struct为public(原因是为了兼容c语言)
注意: 访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
体会:
c++为了兼容c语言,没有抛弃掉struct,在c++中可以将struct当class用,但是为了兼容,在封装性上又是不一样的,又此可以看出struct的缺点,对数据没有保护
类的作用域
类的定义形成了一个以类为单位的作用域。如果需要在类外定义该类成员,就需要使用::作用域解 析符号指明成员属于哪个类作用域
类对象模型
-
类对象的存储方式
只保存成员变量,成员函数存放在公共的代码段
一个类的大小,实际就是该类中成员变量占用内存之和,需要进行内存对齐,空类的大小为一个字节
this指针
-
作用
针对类不同的实例,调用相同的成员函数,由于每个对象都有自己的数据所以需要this指针为成员函数提供的不同的数据,以此表现出不同行为
-
c++引入了this指针
c++编译器给每个成员函数增加了一个隐藏的指针参数,让该指针指向当前的对象,在函数中所以对成员变量的操作,都是由该指针取访问操作的
-
特性
- this指针的类型: 类的类型 *const this // 地址不可改变
- 只能在成员函数的内部使用
- this指针本质上是一个形参,是对象调用成员函数时,将对象地址作为实参传递给this形参,对象中不存储this指针
- this指针一般通过ecx寄存器自动传递
类的6个默认成员函数
1. 构造函数
-
作用: 初始化实例
-
构造函数,名字与类名相同,创建类类型实例由编译器自动调用,保证每个数据成员都有一个合适的初值,在实例的生命周期中只调用一次,构造函数并没有开辟空间,只有初始化的功能
-
特征
- 函数名与类名相同
- 无返回值
- 实例实例化时编译器自动调用对应的构造函数
- 构造函数可以重载
class Date { private: int year_; int month_; int day_; public: // 无参 Date(){} // 带参数 Date():year_(1900), month_(1), day_(1){} };
-
使用
// 无参 Date d1; // 带参 Date d2(2019, 5, 5);
*注意:*如果通过无参构造函数创建实例时,实例后面不要跟括号,否则就成为函数声明
如果类中没有显式定义构造函数,则c++编译器会自定生成一个无参的默认构造函数,一旦显式定义编译器不在自动生成
注意: 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造只能有一个
2. 析构函数
-
作用: 对象在销毁时会自动调用析构函数,完成类的一些资源清理工作
-
特征
- 析构函数名是在类名前加上字符~
- 无参数无返回值
- 一个类有且只有一个析构函数,若未显示定义,系统会自动生成默认的析构函数
- 对象的生命周期结束后,c++编译器系统会自动调用析构函数
~Date() { }
3. 拷贝构造函数
-
作用: 对已有实例拷贝生成一个跟被拷贝实例一样的实例
-
特征
- 拷贝构造函数时构造函数的一个重载形式
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用
- 若未显示定义,系统生成默认的拷贝构造函数。默认的拷贝构造函数将对象在内存存储方式按字节序进行拷贝,是浅拷贝
- 在对类中成员变量有指针变量等需要深拷贝变量时,需要自定义拷贝构造函数
Date(const Date& date) { year_ = date.year_; month_ = date.month_; day_ = date.day_s; }
4. 赋值运算符重载
运算符重载
-
作用: c++为了增强代码可读性引入了运算符重载,目的是为了自定义类型可以使用自定义运算符规则。运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名称以及参数列表,其返回值类型与参数列表和普通函数类似
-
特征
- 关键字operator后面直接接需要重载的运算符
- 函数原型:type operator操作符(parma)
-
注意:
- 不能通过连接其他符号来创建新的操作符:如operator@
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变
- 作为类成员的重载函数时,其形参会比操作数数目少一个参数,因为有this
- .* \ :: \ sizeof \ ?: \ .不能重载
赋值运算符的重载
-
特征
- 参数类型
- 返回值
- 检测是否自我赋值
- 返回*this
- 一个类如果没有定义赋值运算符重载,编译器也会自动生成, 按照字节序拷贝
Date& operator=(const Date& date) { if (this != &date) { year_ = date.year_; month_ = date.month_; day_ = date.day_; } return *this; }
注意:
-
参数加const, 限制函数中传入引用参数的更改
-
返回值是被赋值者的引用,即*this
-
这样避免了一次拷贝
-
可以实现连续赋值 a=b=c
如果不是返回引用而是返回值类型,那么在执行b=c后,返回的是值类型,会对*this进行一次拷贝,会生成一个匿名对象,这时这个匿名对象就是一个右值,但是匿名对象的生命周期在函数结束后会被销毁,所以再执行a=就会出错
-
-
当赋值的对象已经存在的时候是调用赋值重载函数,如果赋值的对象不存在则调用的是拷贝构造
const成员
- const对象不能调用非const成员函数
- 非const对象可以调用const成员函数
- const成员函数内不可以调用其它的非const成员函数
- 非const成员函数内可以调用其它const成员函数
取地址及const取地址操作符重载
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
这两个运算符一般不需要重载,只有在想暴露特定内容才需要重载