析构函数和构造函数

Posted lune-qiu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了析构函数和构造函数相关的知识,希望对你有一定的参考价值。

1、构造函数和析构函数为什么没有返回值?

总是由编译器来调用这些函数以确保它们被执行。如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能由客户程序员自己来显式的调用构造函数与析构函数,这样一来,安全性就被人破坏了。另外,析构函数不带任何参数,因为析构不需任何选项。

构造函数返回的应当是所构造的对象。否则,我们将无法使用临时对象初始化对象。

 

class C

{

public:

    C(): x(0) {}

    C(int i): x(i) {}

private:

    int x;

};

正常情况下:C c = C();是用默认构造函数创建一个临时对象,并用这个临时对象初始化c。但是如果构造函数有返回值则用返回值去初始化,就乱套了。

即调用带参数构造函数C::C(int i)。得到的c.x便会是1。于是,语义产生了歧义。???

 

void f(int a) {...}  //(1)
void f(const C& a) {...} //(2)
f(C()); //(3),究竟调用谁?
对于(3),我们希望调用的是(2),但如果C::C()有int类型的返回值,那么究竟是调(1)于是,我们的重载体系,乃至整个的语法体系都会崩溃。

 

2、显式调用构造函数和析构函数

class A
{
public:
    A()
    {
        cout<<"构造"<<endl;
    }
    ~A()
    {
        cout<<"析构"<<endl;
    }
};
int main()
{
    A a = A();
    A *tmp = new A;
    tmp->~A();//显示调用
    delete tmp;
    return 0;
}

结果:

  构造

  构造

  析构  //这个是显示调用的析构函数

  析构  //这个是delete调用的析构函数

  析构

这有什么用?有时候,在对象的生命周期结束前,想先结束这个对象的时候就会派上用场了。直接调用析构函数并不释放对象所在的内存。

显示调用构造函数方法有两个:

第一:pMyClass->MyClass::MyClass();
第二:new(pMyClass) MyClass();

第二种用法涉及C++ placement new 的用法。参考:http://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html

显示调用构造函数有什么用?

有时候,你可能由于效率考虑要用到malloc去给类对象分配内存,因为malloc是不调用构造函数的,所以这个时候会派上用场了。
另外下面也是可以的,虽然内置类型没有构造函数。???
int* i = (int*)malloc(sizeof(int));
new (i) int();

3、拷贝(复制)构造函数为什么不能用值传递

class S
{
public:
    S(int x):a(x){ }
    S(const S st) //拷贝构造函数
    {
        a = st.a;
    }
private:
    int a;
};
int main()
{
    S s1(2);
    S s2(s1);
    return 0;
}

当给s2初始化的时候调用了s2的拷贝构造函数,由于是值传递,系统会给形参st重新申请一段空间,然后调用自身的拷贝构造函数把s1的数据成员的值传给st。当调用自身的拷贝构造函数的时候又因为是值传递,所以...
也就是说,只要调用拷贝构造函数,就会重新申请一段空间,只要重新申请一段空间,就会调用拷贝构造函数,这样一直下去就形成了一个死循环。所以拷贝构造函数一定不能是值传递。

4、构造函数/析构函数抛出异常的问题

构造函数抛出异常:
    1.不建议在构造函数中抛出异常;
    2.构造函数抛出异常时,析构函数将不会被执行;

如果你的对象需要撤销一些已经做了的动作(如分配了内存,打开了一个文件,或者锁定了某个信号量),这些需要被撤销的动作必须被对象内部的一个数据成员记住处理。

析构函数抛出异常:
    在有两种情况下会调用析构函数。第一种是在正常情况下删除一个对象,例如对象超出了作用域或被显式地delete。第二种是异常传递的堆栈辗转开解(stack-unwinding)过程中,由异常处理系统删除一个对象。
在上述两种情况下,调用析构函数时异常可能处于激活状态也可能没有处于激活状态。遗憾的是没有办法在析构函数内部区分出这两种情况。因此在写析构函数时你必须保守地假设有异常被激活,因为如果在一个异常被激活的同时,析构函数也抛出异常,并导致程序控制权转移到析构函数外,C++将调用terminate函数。这个函数的作用正如其名字所表示的:它终止你程序的运行,而且是立即终止,甚至连局部对象都没有被释放。

概括如下:
    1.析构函数不应该抛出异常;
    2.当析构函数中会有一些可能发生异常时,那么就必须要把这种可能发生的异常完全封装在析构函数内部,决不能让它抛出函数之外;
    3.当处理另一个异常过程中,不要从析构函数抛出异常;

每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A产生四个缺省的函数,如
A(void); // 缺省的无参数构造函数
A(const A &a); // 缺省的拷贝构造函数
~A(void); // 缺省的析构函数
A & operate =(const A &a); // 缺省的赋值函数

既然能自动生成函数,为什么还要程序员编写?
原因如下:
1)如果使用缺省的无参数构造函数缺省的析构函数,等于放弃了自主初始化清除的机会,C++发明人Stroustrup的好心好意白费了。
2缺省的拷贝构造函数缺省的赋值函数均采用位拷贝而非值拷贝的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。























以上是关于析构函数和构造函数的主要内容,如果未能解决你的问题,请参考以下文章

构造函数和析构函数

c++ 复制构造函数和析构函数

php 中构造函数和析构函数

php构造函数的PHP 5 构造函数和析构函数

C语言里面构造函数和析构函数的运用办法

c ++的构造函数和析构函数汇编[重复]