C++构造析构

1.构造函数

-注意,面向对象的思想都是针对编译器来说的,由编译器为我们翻译成相应的程序,机器只能识别其自己的机器指令。

-创建每一个对象时会自动调用一个成员函数,称为构造函数,也就是说每次定义一个对象时都会自动的默认运行这个函数,不写的话系统会自动的生成一个什么都不做的无参构造函数函数名就使用类名,无返回类型。

 

-在类中的函数支持重载,就是说同名的函数会根据参数的不同编译器自动调用相应的函数,构造函数可以重载一般访问限制为公开,如果一个类的构造函数被private修饰后,就不能够用其在外部创建对象了,因为创建对象需要调用构造函数,而构造函数又被私有了。如果想创建对象可以在类中,因为类里面的函数可以调用私有的成员。这就是单例模式的实现机制。

 

-创建对象的时候,要根据构造函数的参数类型来创建传递实参数,如果一个类只有一个没有形参的构造函数,那么在栈中创建对象的时候,就不能在创建的时候传递参数,而且如果创建对象的时候不传递参数,后面的括号不写,否则就默认是函数声明,不会创建对象。

-如果一个类中只有带参数的构造函数,那么创建对象的时候,全部都必须带括号传递参数初始化。一般的类都有多种构造函数,可以用来重载,选择怎样的创建方法,就会自动的调用相应的构造函数。

 

-如果一个类没有构造函数,编译器会自动为他产生一个不干事的空构造函数。但是只要有构造函数了,编译器就不再为这个类产生无参的构造函数(默认构造函数、缺省构造函数)。

 

people p("cao",20); people*p1 = new people("fei",21);

注意这两种方法开辟的变量的不同,一个在栈中,一个在堆中。

一个对象创建时如果需要做额外的事情,就可以放在构造函数里面。

 

创建对象的实际操作步骤:分配存储器,执行构造函数。先给这个对象分配内存,然后用构造函数初始化对象。

 

this调用非静态成员函数时,编译器总是自动悄悄把调用它的那个对象的址作为一个隐含参数传递,在成员函数内部,通过关键字this来接受这个隐含参数,是一个指针使用方法是:this->成员,比如一个类定义了很多对象,这些对象的地址是不同的,每个对象都可以调用类中的成员函数实现一定的操作,但是当调用的时候,这个对象的地址就被赋值到this了。在构造函数中也可以使用this访问对象的成员数据。

注意静态成员中不能有this指针。

*this.成员

 

 

 

 

 

 

class cat{

public:

    cat() //要自己定义,一旦自己写了构造,默认的无参构造就不会产生了

{ cout<<"call cat()"<<endl;}

cat(string n)

{

cout<<n<<endl;

}

};

cat a;//构造函数重载,调用第一个无参的构造初始化

cat b("mimi");//构造重载,调用第二个有参的函数初始化

cat c();//不传递参数,被编译器认为是函数声明,不会创建对象不会调用构造函数

 

 

注意:不管是无参构造函数,还是拷贝构造函数,还是自己写的构造函数,都是类的构造函数,他们的函数名都是类名,无返回值,参数不同,构成重载的关系,调用了任何一个都不会调用别的构造函数了。

 

--构造函数的初始化列表

构造函数定义里紧跟参数表括号之后可以有一个初始化列表,用冒号开头,后面有若干对“成员变量(初始值)”,多对之间用逗号分隔。初始化列表是初始化常量成员引用成员的不二法门。因为他们都是要求在定义的时候必须初始化的,所以要放在初始化列表中做,注意初始化列表中变量的初始化顺序与其在初始化列表中的位置无关,只与其声明的位置有关系。初始化列表先于构造函数体先执行。

class File

{

string path;

public:

File(const string& path):path(path)

{     

//冒号后面第一个path是成员 ,第二个是形参 ,这样下面就不用再去初始化成员变量了

cout<<"创建档"<<path<<endl;

}

};

 

 

 

2.析构函数

 

-析构函数:对象释放时会自动调用的函数,函数名为: ~类名,总是无参,无法重载,无返回类型。一般也是公开的,这样可以利用它在释放对象时调用做些什么。

 

-默认析构函数:如果一个类没有定义析构函数,编译器会自动产生一个什么也不做的析构函数。

 

-如果想在main之前或者main之后做一些工作,可以用全局对象的构造函数和析构函数来实现。因为全局对象的生命周期就是到主函数结束。

-对象本身占用的存储器空间会在超出作用范围时或者delete时自动释放了,如果对象还额外分配了资源,可以在构造函数中释放那些额外的资源。

 

 

 

 

 

 

3.继承中的析构与构造

 

 

用子类初直接初始化父类,其实用的是其拷贝构造函数,在创建父类的时候,A(A&),A&就是B传进来的,然后进行初始化。创建父类的对象。

 

继承中,子类不能继承父类的构造函数,可以继承父类的析构函数,称为自己的析构函数,这点已经验证过,如果子类不定义自己的析构函数,那么在析构时,会调用两次父类的析构函数,因为子类继承了父类的析构函数。

 

 

 

 

 

在等效模型中,struct中只有变量的值,那些函数不是这里的。

 

 

 

类中的成员变量可以是其他类的成员。如果一个类继承自父类并且有其它的对象作为成员,那么构造函数如何调用。口诀:先父母,后客人,再自己。

 

class object

{

public:

object(const char*s)

{

cout << "object()"<<"  " << s << endl;

}

};

 

class parent :public object

{

public :

parent(const char*s):object(s)

//注意子类在自己执行构造函数时,会先执行父类的构造函数,执行的是默认的无参构造函数,如果自己子类中没有无参构造函数,这里就要手动的调用父类有残的构造函数来初始化父类成员。

{

cout << "parent()" << "  " << s << endl;

}

};

 

class child :public parent

{

protected:

object o1;

object o2;

public:

child():o2("o2"),o1("o1"),parent("parameter is from child!")

{

cout << "child()" << "  " << endl;

}

};

 

int main()

{

child c;

system("pause");

 

}

 

注意,初始化列表中的顺序没有任何作用,o1 o2的构造函数调用顺序只跟在类中定义的先后顺序有关

4.C++中子类调用父类的构造函数方法

构造方法用来初始化类的对象,与父类的其它成员不同,它不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但不继承父类的构造方法)。因此,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。

如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式地声明构造函数情况下创建。

构造原则如下:

1. 如果子类没有定义构造方法,则调用父类的无参数的构造方法。

2. 如果子类定义了构造方法,不论是无参数还是带参数,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。

3. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数,则会调用父类的默认无参构造函数。

4. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。

5. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。

6. 如果子类调用父类带参数的构造方法,需要用初始化父类成员对象的方式,

 

 

 

 

 

 

 

 

 

 

 

 

 

5.继承中的变量同名问题

 

 

 

 

6.构造析构与虚函数

我们都知道,虚函数时用来实现多态的,且多态实现必须有重写,和父类指针或引用指向子类。如果虚函数不被重写也就没有意义。

 

1.析构函数一般需要设置成虚函数?

尤其在多态的使用中,防止内存泄露。如果子类对象赋值给父类指针,那么这个父类指针在delete的时候,会先调用子类的析构函数,然后调用父类的析构函数,如果不是这样的话,就会只调用父类的,但是子类的就没有调用。

 

2.构造函数不能为虚函数?

1,从存储空间角度
    含有虚函数的类对象都会有一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有初始化,无法找到vtablevtable是在构造函数中才初始化的所以构造函数不能是虚函数。

3、构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它。但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。

4、从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数  从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数

5、当一个构造函数被调用时,它做的首要的事情之一是初始化它的V P T R。因此,它只能知道它是当前类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码- -既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。

        所以它使用的V P T R必须是对于这个类的V TA B L E。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内, V P T R将保持被初始化为指向这个V TA B L E, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置V P T R指向它的 V TA B L E,等.直到最后的构造函数结束。V P T R的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生 类顺序的另一个理由。
        但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置V P T R指向它自己的 V TA B L E。如果函数调用使用虚机制,它将只产生通过它自己的V TA B L E的调用,而不是最后的V TA B L E(所有构造函数被调用后才会有最后的V TA B L E)。

 

 

3.构造函数中为什么不要调用虚函数

 

首先构造函数虽然不能玩诶虚函数,但是其内部可以调用虚函数,但是没有意义。

构造函数中调用虚函数也不会实现多态。应该是直接

指的是直接或者隐式使用 this指针调用虚函数由于,不能确定this指针所代表的对象,包括其中指向虚函数表的指针,有没有构造完成,即包括指向虚函数表的指针在内的所有成员有没有,完全构造完成,所以难以正常使用虚函数。

有标志也不行,子类会更改,虚函数表指针;
虚函数表指针,会随着父类->子类;逐层构造,逐渐更改

这个说法有问题,父类正在构造时候,子类对象,并没有开始构造,父类对象构造完成后,才会开始子类的构造,这时才会把虚函数表的指针,改成子类的虚函数表指针,这是单继承。
多继承更复杂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值