C++复习10.对象的初始化拷贝析构函数

对象的初始化、拷贝构造和析构函数 20131002

构造函数、析构函数、赋值函数是类的基本函数。每一个类只有一个析构函数,但是可以有多个构造函数、多个赋值函数。一般如果类中没有显示的声明和定义上述函数,C++编译器会自动为其生成4个public inline默认函数A(), A(const A&), ~A(), A& operator=(constA& a)

1.构造函数和析构函数的起源

         C++编译器有更加严格的类型安全检查机制,几乎可以找到程序的所有语法错误。但是很多错误因为变量没有争取的初始化或者清除导致,而初始化和清除的工作经常被遗忘,所以有了类的构造函数用于初始化数据成员,当创建对象的时候,构造函数自动被调用执行;当对象消亡的时候,析构函数被自动调用。因此不要在构造函数中做那些和初始化对象无关的工作,也不要在析构函数中做哪些和销毁对象无关的工作。

2.为为什么需要构造函数和析构函数

         编译期间无法确定一个程序运行期间会创建什么类型的对象,对象的初始化工作最好是通过一个函数执行,而且是在对象创建的同时,这就是构造函数,同样对象销毁的时候,需要一个函数销毁对象的资源,就是析构函数。

         创建一个变量和动态对象的时候,一定不要忘记初始化,否则会直接访问原始的内存,出现错误。初始化工作是在对象创建的同时使用初始值直接填充对象的内存单元,银子中间不会有数据类型转化的中间过程,也不会产生临时对象;但是赋值操作是在对象创建好的时候,进行的赋值操作,过程可能需要类型转换,也就是会产生临时变量。

         构造函数一个用途还有给一些可能存在的隐含成员创建一个初始化的机会如vptr,否则虚拟机制将无法实现。

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

         一般我们会在构造函数中初始化成员数据成员,这不是真正意义上的初始化,而是赋值。真正的初始化是使用的所谓的“初始化表达式表”进行的,初始化列表是在构造参数表之后,在函数体{}之前。这说明该列表的初始化工作发生在函数体内的任何代码被指向前,而且编译器也是这样做的。

         构造函数的初始化列表使用规则:

         1)如果类之间存在继承关系,派生类可以直接在初始化列表中调用基类中的特定构造函数向他传递参数,因为我们不能在初始化对象的时候访问基类的数据成员。

         class A{public: A(int x);};

         class B: public A{B(int x, int y);};

         B::B(int x,int y): A(x){….}

         2)类的非静态const数据成员和引用成员只能够在初始化列表中初始化,因为他们只存在初始化语义,而不存在赋值语义。

         3)类的数据成员的初始化可以采用初始化列表或函数体内赋值的两种方法,但是两者效率不完全相同。当传递的是一个类型的参数进行初始化的时候,建议使用初始化列表:

         B::B(const A & a): m_a(a){….}

         B::B(const A & a){m_a= a}

         对于第一种方式,类B的构造函数在初始化类表中调用了类A的拷贝构造函数进行初始化;但是对于第二种方式,手下先创建一个m_a对象(调用A的默认构造函数),在调用A的复制函数,在将参数a赋值给m_a。

4.对象的构造和析构次序

         这里的构造和析构次序不是给对象分配的内存空间的次序,而是说对象的初始化和销毁的次序。如果一个类中没有基类,他的构造过程十分简单。但是对于派生类,他的构造函数将首先调用它的基类的构造函数,因此任何一个对象都是首先构造最根类的子对象。

         析构函数会严格按照和对象构造的相反次序指向,器而是唯一的。数据成员的初始化次序完全不受他们在初始化列表中的出现次序的影响,只由他们在类中声明的次序决定

5.构造函数和析构函数的调用时机

         非静态的局部对象: 程序执行贷该对象的定义的时候,创建对象并且调用相应的构造函数,如果没有提供初始值,则调用默认的构造函数;程序执行到对象的生存域的时候暗中调用对象的析构函数。

         静态的局部对象:如果没有默认的构造函数,自动初始化为0,直到程序结束的时候才会调用析构函数。

         全局对象:在程序进入main 之前自动调用他们对应的构造函数进行初始化,但是初始化的顺序不确定;如果没哟默认的构造函数则会初始化为0,直到main函数结束之后才会调用析构函数。

         类的静态数据成员对象:等同于全局对象的情况

         对象的引用:初始化和销毁都不会调用构造函数和析构函数,也正是因为如此,引用传递参数比传递只高效;

         动态对象的创建:在new的时候调用构造函数,在delete的时候调用析构函数

         对象的赋值:调用类型匹配的operator=重载函数。

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

         C++中允许实现多个构造函数,即构造函数的重载,可以实现不同的方式初始化对象。不能够同时定义一个无参数的默认构造函数和一个参数全部都会有默认值的构造函数,这样会出现二义性的问题。

         拷贝构造函数式这样的构造函数:第一个参数是该类型对象的引用,const引用, volatile引用和const volatile引用,并且不存在其他的采纳数或者其他的参数都有默认值。 拷贝构造函数的参数必须是同类对象的引用,而不是对象值。

         类的赋值函数也是一种拷贝函数,当然也可以实现重载:

         A& operator=(const A& a);

         A& operator=(A & copy);

         A& operator=(A copy);

          最后一种方式会调用当前类的构造函数。

7.String类的构造函数和析构函数

         C++中的初始化的值是‘/0’,而不是NULL,因此在任何的C++string中大小至少为1;空字符串也是有效的字符串,长度是1

8.拷贝构造函数和赋值函数,在需要深复制的时候,需要重写这些函数,如string 指针等等。

9String类型的赋值函数

String & String::operator=(const String & other){

         If(this != other){

         Char * temp = new char[strlen(other)+1];

         strcpy(temp,other);

         delete []m_data;

         m_data= other;

}

}

10阻止编译器自动生成,我们需要手动实现这两个函数,同时将函数体内部置空即可,同时可以设置访问权限是private。

11.基类的构造函数、析构函数赋值函数都是不能够被子类继承的,如果类之间存在继承关系需要注意:

         派生类的构造函数应该在初始化的列表中中显示的调用基类的构造函数(除非基类的构造函数可以不访问);

         如果基类是多态类,必须把基类的析构函数声明在虚函数,这样就可实现动态的绑定,否则会造成内存泄露。

class Base{

public:

    Base(){

        cout << "Base::Base()" << endl;

    }

    virtual ~Base(){

        cout << "Base::~Base()" << endl;

    }

};

class Derived:public Base{

public:

    Derived(){

        cout << "Derived::Derived()" << endl;

    }

    virtual ~Derived(){

        delete p_base;

        cout << "Derived::~Derived()" << endl;

    }

private:

    Base *p_base;

};

 

int main()

{

    Base * b = new Derived;

    delete b;

    return 0;

}

追梦的飞飞

于广州中山大学20131002

原文地址:https://www.cnblogs.com/hbhzsysutengfei/p/3409460.html