虽然平时我们都叫构造函数和析构函数,但他们并不像真正的函数一样,不管是构造还是析构两个都没有返回值,所以最好称他们为构造器和析构器。基础就不说了,下面分别说一下这两个东西中容易被忽视的东西:
一、构造器
1.我们知道构造器若没有定义,编译器会自动生成一个默认无参的构造器,一旦自定义了构造器,那么就不再会有自动生成,一般情况下最好自定义中带有无参的构造器,不然bug是不可避免的,建议是定义带默认参数的构造器,例如:
class A{
private:
char* a;
public:
A(int len = 10){
a = new char[len];
}
};
上面列举了一个简单的带默认参数的构造器的例子,这样在创建对象时,可以不带参数也可以带一个参数,想带多个参数的情况类似。显然构造器必须放在public下,不然怎么创建对象。
构造器的重点在于它的目的是初始化对象,那么是如何分配空间的呢?
首先先创建一个对象,如下:
A *p = new A;
一newA就创建一个A类的对象,p指向这个对象的首地址,也就是说类A中char* a的地址,这时候给对象一个4字节的地址空间(指针在32位下是4个字节),然后在构造器中又给a了一个长度为len的内存空间,其形象的表示如下图:
根据上面的说明,显然构造器并不只是开辟了C2这么一个对象的内存空间,同样也为a开辟了空间,是一连串的效应。
刚才也说过了,如果自定义了构造器,那么编译器就不会自动生成无参的构造器,这时候如果没有定义无参构造器,那么当创建对象数组时,若没有初始化,就会报错。
class B{
private:
string s;
public:
B(string str): s(str){}
};
int main{
B b[5];
B c[2] = {B("acd"), B("dasf")};
}
这种完全不初始化,或者不完全初始化都会报错。
把B类的构造器改成这样:
class B{
private:
string s;
public:
B(string str = ""): s(str){}
};
int main{
B b[5];
B c[2] = {B("acd"), B("dasf")};
}
就没错了。
所以强烈推荐使用带默认参数的构造器。
二、析构器
析构器与构造器的作用相反,是用来释放对象的。构造器先开辟C2,再开辟C3,而析构器则先释放C3,再释放C2:跟上面对应,释放如下:
delete p;
再小结一下什么时候才会调用析构器:
1、当对象所在的作用域结束的时候,也就是说达到所在作用域的“}”符号的时候。
2、当用delete语句释放对象的时候。