对象的初始化,拷贝和析构

来源:互联网 发布:同花顺股票模拟软件 编辑:程序博客网 时间:2024/05/22 14:21

每个类只有一个析构函数,但可以由多个构造函数。

对于一个类,如果程序员不显式的声明定义上述函数,编译器自动的产生4个inline函数,

A();A(const A& );~A(); A& operator=(const A& )


1.不要在构造函数内做与初始化无关的工作,不要在析构函数内做与销毁对象无关的工作。

注意初始化和赋值的区别

初始化:在对象创建的同时使用初值直接填充对象的内存单元。因此不会产生类型转换等中间过程,也就不存在临时对象的产生

赋值:在对象创建好后任何时候都可调用的函数,"="运算符函数,存在类型转换,也会产生临时对象。


2.构造函数的成员初始化列表

构造函数体内部初始化数据成员,实际是赋值,而不是真正意义上的初始化。

真正的初始化是使用初始化列表,该列表的初始化工作发生在函数体内的代码被执行之前。

注意:派生类的构造函数可以在初始化列表中调用父类的构造函数,进行初始化。这样效率更高。


3.对象的构造与析构的次序

任何一个对象总是最先调用最根类的子对象,然后逐层向下扩展,知道把整个对象构造起来。析构会严格按照对象构造的相反次序执行析构函数。


4.构造函数与析构函数的调用时机

静态局部对象: 如果没有提供初始值,会调用默认构造,初始化成0.提供初始值就有拷贝构造函数。

全局对象:程序进入main之前自动调用他们相应的构造函数来初始化,但是初始化顺序不确定。提供初始值则调用拷贝构造函数,否则调用默认构造函数。main结束调析构。

对象的引用:其初始化和销毁都不会调用构造函数和析构函数。

动态创建对象:new运算符创建时,自动调用匹配的构造函数。返回地址的指针是否为静态对应各自的情况。delete运算符时会自动调用析构函数。

对象赋值:调用operator=函数。


5.构造函数和赋值函数的重载

构造函数;

       默认构造函数:参数为空,或者参数都有默认值,二者不能同时存在会产生二义性。

       拷贝构造函数:第一个参数为本类对象的const引用,volatile引用,const volatile引用,并且没有其他的参数,或者其他参数都有默认值。

注意:如果没有显式的定义默认构造函数,却定义了带参数的构造函数,那么后者的存在就会阻止编译器生成前者。


6.拷贝构造函数的危险

 编译器会自动产生一个"按成员拷贝"的方式自动生成相应的默认函数。如果类中含有指针成员或者引用,将会发生隐含错误。所以要自己去写拷贝构造函数。

注意:拷贝构造函数是在对象被创建并用一个已经存在的对象来初始化它时调用的,而赋值函数只能把一个对象赋值给一个已经存在的对象,使得那个已经存在的对象

和源对象有相同的状态。

string a("hello")

string b("world");

string c=a//拷贝构造,最好写成string c(a)

c = b;//赋值


7.偷懒的办法来处理拷贝构造函数和拷贝赋值函数

将拷贝构造函数和拷贝复制函数声明为private,并且不实现他们,防止用户调用,防止友元函数和成员函数调用。

甚至可以把所有的构造函数和赋值函数都声明为private,这样就彻底阻止了该类的实例化,或者默认构造函数声明为private,而把其他的带参构造函数声明为public:

这样就强迫用户必须使用带参的构造函数来声明和定义对象。


8.如何实现派生类的基本函数

1.派生类的构造函数应在其初始化列表里显式的调用基类的构造函数(除非基类的构造函数不可访问)

2.如果基类是多态类,必须把基类的析构函数定义成虚函数,这样就可以像其他虚函数一样实现动态绑定,否则可能发生内存泄漏。

#include <iostream>class Base{ public:  virtual ~Base(){cout << "~Base()" << endl;}};class Derived:public Base{ public:  virtual ~Derived(){cout << "~Derived()" << endl;}};int main(){  Base* p = new Derived;  delete p;}
输出结果

~Derived()

~Base()

如果基类不是虚析构函数,那么输出结果

~Base()

派生类对象的内存单元无法释放,造成内存泄漏。


3.编写派生类的赋值函数时,注意不要忘记对基类的数据成员重新赋值,这可以通过调用基类的赋值函数实现。



0 0
原创粉丝点击