4个默认函数
- A() // 自定义可以自主初始化
*~A() // 自定义可以自主清除
*A(const A&) // 基本类型的变量的拷贝和赋值都是按bit 进行,这样,两个对象中指针成员指向的对象是同一个。
*A& operator=(const A&) // 默认赋值函数, 需要注意,初始化不同于赋值。
建议
最好为每个类显示地定义构造函数和析构函数,即使它们暂时空着,尤其是当类含有指针成员或引用成员的时候。
构造函数的初始化列表 和 函数体内赋值。
规则
初始化列表中调用基类的特定构造函数
B::B(int x, int y):A(x) {...}
类的非静态const数据成员和const 引用成员的初始化只能在初始化列表中, 因他们只有初始化,无赋值。
A(int id):id_(id)
对于内部数据类型,初始化列表或函数体内赋值的效率几乎没有差别,但构造函数有其他类的对象,使用初始化列表方法效率高。
如B的时候,使用函数体内赋值,实际上B的构造函数做了两件事,默认构造函数+赋值函数。
如果有依赖关系,分别放在初始化列表和函数体内。
不同对象类型的构造和析构调用时机
对象类型 | constructor | deconstructor |
---|---|---|
非静态局部对象 | 执行到初始化定义的时候 | 对象生存域结束的时候 |
静态局部对象 | 对象定义处 | destructor, main() 结束后 |
全局对象, T g_obj; static T g_obj,static 作用域 | 进入main前 | main结束后 |
类的静态数据成员对象 | 等同于全局对象 | 等同于全局对象 |
拷贝构造函数
拷贝构造函数的参数必须是 对象的引用。因如果是值传递,必须调用拷贝构造函数,就会造成不停的循环。
A& copy(const A a) 是语法错误。
赋值函数 operator= 也是一种拷贝函数
- A& operator = (const A& copy)
- A& operator = (A& copy)
- A& operator = (A copy) // 会调用拷贝构造函数。
按需定义构造函数和赋值构造函数
如下图,如果没有自定义拷贝构造函数或赋值构造函数,两个对象指向同一快内存,任何一个改动都会影响其他。对象被析构的时候,会被delete[] 两次。
public String
{
public:
String(const char *str=""); // constructor
~String() {delete[] data_}
private:
size_t size_;
char *data_;
}
Note: 需要检查以避免自赋值。原因可能间接。 p=&a, a=*p;
Note: 需要释放原有的内存资源。避免内存泄露
char *temp = new char[ strlen(other.data)+1];
strcpy(temp, other.data_);
delete[] data_
data_ = temp
Note: 返回 return *this. 返回的是引用。
Note 避免编译器自动生成默认的构造函数。 可以定位为private,但不实现。 如 private: A(const A&a); A& operator= (const A& a)
派生类
构造函数 | 析构函数 | 拷贝或赋值函数 |
---|---|---|
派生类的构造函数在初始化列表中显示地调用基类的构造函数 | 基类的析构函数必须为虚函数,否则派生类的内存单元可能不能被释放 | 派生类的赋值函数需要显示的对基类成员进行赋值。 Base::operator = (other); |