一、构造函数
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照样正常使用。
四、总结
在一个类拥有资源(堆,或其它系统资源)的情况下,应该使用深拷贝,否则可以使用浅拷贝。