前言
本篇重在阐述关于C++的类的一些细节,不作入门教学,仅做个人总结。
正文
类
- 空类的大小为1个字节,用于唯一标识这个类
- 类的大小遵循内存对齐的规则(即内存要对齐到某一个数值)
- 若存在内部类,内部类仅是一个声明,并不占据本类的大小
- 成员变量与成员函数分开存储,成员函数并不占类的大小
类的成员
1. this指针:关于this指针介绍
2. 类默认提供的成员函数:
tips: 若成员函数的声明和定义都放在类中,编译器可能会将其当做内联处理
explicit关键字:用于修饰构造函数,修饰后该类则不能隐式调用构造函数进行类型转换,必须显式调用
-
构造
-
析构:无返回值,无参数
-
拷贝构造(构造函数的重载版本)
深浅拷贝
浅拷贝:
默认提供的拷贝构造即为浅拷贝,对内置类型执行以字节为单位的复制,自定义类型会调用其拷贝构造。
(由于指针类型为内置类型,进行浅拷贝会导致两个指针指向同一个内存空间,故此出现了深拷贝的操作)
浅拷贝带来的问题:
1. 会对同一块内存进行delete
2. 修改不同的指针内容时,实际是对同一块内存进行修改,会导致修改的内容丢失
深拷贝方式:
1. 创建一个新的指针/新的空间,直接将原先指针指向的值赋值给该指针
2. 利用构造函数构造一个临时对象,将临时对象的数据拷贝到要构造的对象上
3. 利用引用计数和写时拷贝解决,详见:写时拷贝- 拷贝构造的参数注意是引用类型,否则会导致无限递归
- 当提供了拷贝构造后,类则不会再提供默认构造和拷贝构造函数
class Array {
public:
Array(int len);
Array(const Array& arr); // 拷贝构造函数
~Array();
public:
int operator const { return m_p[i]; } // 获取元素(读取)
int& operator { return m_p[i]; } // 获取元素(写入)
int length() const { return m_len; }
private:
int m_len;
int* m_p;
};
Array::Array(int len) : m_len(len) {
m_p = (int*)calloc(len, sizeof(int));
}
Array::Array(const Array& arr) {
// 深拷贝
this->m_len = arr.m_len;
this->m_p = (int*)calloc(this->m_len, sizeof(int));
memcpy(this->m_p, arr.m_p, m_len * sizeof(int));
}
Array::~Array() {
free(m_p);
}
- 赋值重载
- 取地址操作符重载
- const取地址操作符重载
这三个都是属于运算符重载,平常无需过多关注,默认提供的已经足够使用
初始化列表:进行初始化操作时的最佳选择(详解初始化列表)
必须使用初始化列表的三种场景:
- 未提供默认构造的自定义类型成员需要在初始化列表内显示调用
- 存在const修饰的成员时
- 存在引用类型的成员变量时
无论什么情况下,初始化列表都会先于构造函数体执行,初始化的顺序与声明顺序有关,而与初始化列表中出现的成员顺序无关
3. 静态成员(static修饰的成员)
- 静态成员变量必须在类内声明,类外初始化
特例:const static修饰的整型变量可以在类内进行声明和初始化,只有整型变量可以。 - 静态成员函数没有this指针,只能访问静态成员
4. 友元(破坏了类的封装性,一般少用)
- 友元函数(friend修饰函数)
一个函数可以是多个类的友元,调用时与普通函数相同 - 友元类(在类中使用friend修饰其它的类,被修饰的类则成为友元类)
友元关系无法传递和继承,且是单向的,不具备交换性 - 内部类:天然是具备所在类的友元关系,只要往内部类中传入外部类,就可以访问外部类的所有成员
编译器关于构造函数与拷贝构造的优化
- 若在一个表达式中进行了连续的构造,编译器一般都会优化为1次
- 传值返回时,会调用拷贝构造创建临时对象作为返回值后再销毁函数栈帧
- 当返回值返回后若还需要进行拷贝构造创建对象时则会直接优化成将返回值直接构造到接收对象上(即:将接收对象作为临时对象)