一、构造函数

1、         定义

 

 默认构造函数形式:

    类名(){};

构造函数是每个类必须要有的类初始化函数。如果类中没有显示定义,会自动使用一个默认构造函数。默认构造函数中没有任何实现语句,且默认构造函数无参数(如下):

Class A{

      Int a;

}

此类没有显示定义构造函数,默认构造函数就是:

A(){};

显示构造函数可以如下:

Class A{
      A(int k){a = k;}
      Int a;
}
这种情况下还是会存在一个无参数默认构造函数:A(){}。除非定义一个无参数的构造函数,默认构造函数才会无效,如下:
Class A{
      A(){a = 1;}
      Int a;
}

 

二、复制、拷贝构造函数:

            1、定义

                        默认复制(拷贝)构造函数形式:

类名(const 类名 &实例名){};

其实复制或拷贝都是一个意思,含义相同。就是将一个类对象赋值给另一个对象。如下:

Class A{
            A(){};
            Inta;
}
A a1;
a1.a = 10;
A a2 = a1;
此时a2.a 值为:10;

这种情况我们再熟悉不过了,甚至觉得就应该这样,不这样就奇怪了。可你有没有想过会出现这种现象的理论基础是什么?对,就是复制(拷贝)构造函数在其中起的作用。每个类不但有默认的构造函数和析构函数,还会有一个默认的复制(拷贝)构造函数。正是这个函数完成了我们通常见到的上面情形。复制(拷贝)构造函数如下所示:

Class A{
            A(){};
            A(constA &obj) {a = obj.a};
            Inta;
}

这个例子是显视的写出了默认构造函数的具体实现过程。看到这里也许你会问道,这样的话我们根本就没必要显视定义这个函数了,使用系统默认的就可以了。是的,看到这里你并没有感觉到显示定义复制(拷贝)构造函数的重要性。下面我们来看一个例子:

Class A{
            A(){};
            //A(constA &obj) { str = obj.str; str_len = obj.str_len;};
            Char*str;
            Intstr_len;
}
A a1;
a1. str = new char[100];
a1. str_len = 100;
A a2 = a1;
这里我们将默认复制(拷贝)构造函数屏蔽掉,结果我们会看到:
a2.str == a1.str;

你看到这里不知道心里会不会突然感觉到了一丝的不安,这种不安来自于你对访问无效内存产生的忧虑。

试想一下,如果执行完赋值语句后我们将a1对象释放,在a1的析构函数中势必会释放str指向的内存块。此时,a2中的str指针便指向了一个无效的内存区域,这样在使用过程中会出现一些不可预知的错误(比如崩溃等)。解决这个问题的方法是使用深拷贝。

三、深拷贝、浅拷贝

            在第二部分中我们看到的例子是浅拷贝。下面我们用深拷贝来解决浅拷贝中存在的问题,如下例子:

Class A{
            A(){};
            A(constA &obj) 
{ 
            str_len= obj.str_len;
            str= new char[str_len];
            snprintf(str,str_len, ”%s”, obj.str);
};
            Char*str;
            Intstr_len;
}
A a1;
a1. str = new char[100];
a1. str_len = 100;
A a2 = a1;

此时:a2.str != a1.str;因为各自申请了自己的内存副本。

这样a2中也会保留一个属于自己的内存副本,即使a1释放了,a2中的str照样正常使用。

四、总结
            在一个类拥有资源(堆,或其它系统资源)的情况下,应该使用深拷贝,否则可以使用浅拷贝。