构造与析构

本文详细介绍了C++中构造函数与析构函数的作用及使用场景,包括默认构造函数、拷贝构造函数的特点,手动调用析构函数的情况,以及构造与析构过程中可能出现的异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++的安全性高也体现在提供了构造与析构函数区初始化变量、清除变量。

 

1)          C语言的初始化

l       全局变量和静态变量

对于基本类型如intfloatcharint*,默认初始值分别为00'/0'0。结构体或联合体也会被初始化。

l       除了全局变量和静态变量以外, 其它变量如果没有赋初值, 则默认初始值为内存中的垃圾内容,对于垃圾内容不能有任何假设。

l       malloc()realloc() 动态分配的内存也存在垃圾内容,因此在使用内存前最好都赋初值。

 

注意:如果初始值是随机值,有可能会导致程序崩溃,比如指针是随机值。

 

2)          数组/结构体初始化

int age[10] = {10};

第一个元素被初始化为10,其他为0。如果没有初始化,将是随机值。

 

struct data

{

int a;

float b;
};

           data c[10] = { {1,1.4}};

           其他为0

 

3)          默认构造函数

如果没有定义任何的构造函数,则编译器使用默认构造函数。默认构造函数不能为你将所有数据都清零。

 

一般来说,尽量不用默认构造函数,自己手动定义一个。

 

4)          拷贝构造函数

l       默认拷贝构造函数

系统默认的拷贝构造函数是浅拷贝,即按位拷贝。

l       什么时候需要定义自己的拷贝构造函数

当需要按值传递,并且需要深拷贝时(拷贝指针指向的内容);否则应该禁止拷贝构造。

提示:深层拷贝时,为提高效率,可不用每次都拷贝内存,用写时拷贝(引用计数)的原理,可在改变对象时才创建一个新对象,拷贝内存。

l       如何禁止默认的拷贝构造

1)      只声明拷贝构造,不实现

2)      定义为私有的拷贝构造

l       子类的拷贝构造需要调用基类的拷贝构造

class A

{
};

class B:public A

{

public:

B(const B& b):A(b)

{


}

               };

 

5)          为什么构造和析构没有返回值

l       构造函数

从基本语义上看,构造函数应该返回的是构建的对象本身。编译器知道它该返回什么,对于用户来说它是没有返回值的。

 

如果构造函数允许用户指定类型,假如是int,那么会出现如下的尴尬:

class A

{

public:

int A(){}

};

1)   A a = A(); //类型不匹配

2)   void Func( A& a)

  void Func( int a )

  Func( A() ); //调用的是哪一个呢,这样就会出现歧义。

                      

l       析构函数

析构函数和构造函数不一样,其不需要用户明确的调用它,超出生命周期后其自然被调用,如果在堆上构造的,可能需要delete来间接的调用。

 

从基本语义上来看,一个对象析构后,是不需要知道它的返回值了。

 

6)          什么时候需要手动调用析构函数

l       当用new placement将对象构建在特定内存时,由于它没有分配内存,所以一般不delete,这时需要手动调用对象的析构函数。

关于new placement请参考

http://blog.youkuaiyun.com/yeming81/archive/2010/06/15/5673019.aspx

 

7)          什么时候析构不会被调用

l       对于局部变量,当程序调用exit(n) abort()时,析构函数不会被调用

l       对于全局或局部static对象、全局对象,当程序调用exit(n),析构函数会被调用;当调用abort()时,析构函数不会调用。

关于abort/exit,请参考

http://blog.youkuaiyun.com/yeming81/archive/2010/06/16/5673052.aspx

8)          析构的时机

当超出对象的生命周期时,析构函数被调用;

即使是goto或者nonlocal goto(setjmp/longjmp) 其也会调用(Microsoft C++编译器会,其他可能不会,所以不具备移植性)

 

例子如下

class A

{

public:

A(){}

~A(){}
};

 

          local goto(在一个函数内跳转)

          int main()

{

{

               A a;

               goto Label1;

}

Label1:

cout<<”end”<<endl;

return 0;
}

 

          nonlocal goto(在不同函数内跳转)

           jmp_buffer jmpbuf;

          void Func()

{

cout << “ get in “ <<endl;

A a;

longjmp(jmpbuf, 1);

cout << “ never get here “<<endl;
}

          int main()

{

cout << “ get in main “ <<endl

if( setjmp (jmpbuf ) == 0 )

{

cout<<”set lable”<<endl;

Func();
}

                      else

{

       cout<<” jmp to here”<<endl;

}

}

 

关于C跳转,请参考

http://blog.youkuaiyun.com/yeming81/archive/2010/05/31/5637734.aspx

 

9)          异常

l       全局或局部静态对象的构造和析构函数抛出异常时,是无法被捕获的(因为不知道在哪里捕获),默认将调用terminate函数。

l       当抛出一个异常时,是先要析构对象,才能处理异常的。这时候,如果析构函数再抛出一个异常,异常处理过程被中断,默认将调用terminate函数。

l       当构造函数抛出异常时,即构造函数没有成功完成,对象的析构函数肯定也不会执行。

l       所以,一般构造函数和析构函数不要抛出异常。

 

关于异常的详细介绍,请参考

http://blog.youkuaiyun.com/yeming81/archive/2010/06/16/5673070.aspx

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值