构造函数和析构函数
15. 类和对象的关系。
它们之间的区别就像是整型和整型变量之间的区别一样。我们可以定义多个整型变量,例如 int a,b,c;,并且可以为整型变量赋值。但是不能够为整型赋值。类和对象也是一样的。对象是类的实例化,我们可以为对象赋值,但是不能为类赋值。类只是告诉编译器,在定义对象时如何在内存中为对象分配空间。
16. 构造函数和析构函数的调用。
在 C++中,不能通过构造函数或析构函数名称来直接构建或释放对象。当定义一个对象时会自动调用构造函数。例如:
CBook book; //在栈中构建对象
CBook *pBook = new CBook(); //在堆中构建对象
上述两行代码都会调用构造函数。对于析构函数来说,当对象是在栈中构建时,作用域消失会自动调用析构函数。而在堆中构建对象时,当调用 delete 运算符释放对象会导致析构函数被调用。
总之,构造函数在创建类对象的时候被自动调用,析构函数在使用 delete 运算符或类对象生命期结束时,由系统自动调用。
17. 代码膨胀
下面的代码有哪些不足之处?
class CBook //定义一个 CBook 类
{
public:
double m_Price; //定义数据成员
char *m_BookName;
char *m_Press;
CBook() //默认构造函数
{
m_BookName = new char[128];
m_Press = new char[128];
memset(m_BookName, 0, 128);
memset(m_Press, 0, 128);
strcpy(m_BookName, "Visual C++");
strcpy(m_Press, "电子工业");
m_Price = 0.0;
}
~CBook() //析构函数
{
//...
delete [] m_BookName;
delete [] m_Press;
}
};
int main(int argc, char* argv[])
{
CBook book; //定义一个 CBook 类对象 book
int state = 0; //定义一个整型变量
switch(state)
{
case 0: //分支判断
{
printf("%s\n",book.m_BookName); //输出信息
return 0; //函数结束
}
case 1: //分支判断
{
printf("%f\n",book.m_Price); //输出信息
return 0; //函数结束
}
default: //默认情况
return 0; //函数结束
}
return 0;
}
由于 CBook 类的析构函数是内联成员函数,因此上述代码在每一个 return 语句之前,析构函数均会被展开。因为 return 语句表示当前函数调用结束,book 对象的生命期也就结束了,自然调用其析构函数。根据上述分析,main 函数中 switch 语句的编写是非常不明智的,下面对其进行修改,将 return 语句替换为 break 语句。
int main(int argc, char* argv[])
{
CBook book; //定义一个 CBook 类对象 book
int state = 0; //定义一个整型变量
switch(state)
{
case 0: //分支判断
{
printf("%s\n",book.m_BookName); //输出信息
break; //函数结束
}
case 1: //分支判断
{
printf("%f\n",book.m_Price); //输出信息
break; //函数结束
}
default: //默认情况
break; //函数结束
}
return 0;
}
18. 转换函数
请完成下面代码实现 main 函数中的类型转换(不能使用运算符重载)。
class CPerson
{
public:
int m_nAge;
CPerson()
{
m_nAge = 0;
}
};
int main(int argc, char* argv[])
{
CPerson person = 10; //将该语句合法化
return 0;
}
构造函数有一个特殊的功能,就是实现类型转换。但是要求构造函数必须只能有一个参数。当该类型的参数赋值给类对象时,将实现类型转换。这样的构造函数也被称为转换函数。
通过构造函数来实现类型转换。修改 CPerson 类的构造函数,使其包含
一个整数作为参数。例如:
CPerson(intAge)
{
m_nAge = Age;
}
这样,main 函数中的“CPerson person = 10;”语句就合法了。
19. C++ 中的 explicit 关键字有何作用?
explicit 关键字的作用是禁止将构造函数作为转换函数。例如,如果一个类的构造函数中只包含一个整型参数,在构造函数前使用 explicit 关键字可以阻止像 CPerson person = 10;
这样的语句执行,导致编译错误。
20. 调用构造函数
以下代码中输出结果是 0 吗,为什么?
struct A
{
int m_i;
A( int i )
: m_i(i)
{
//...
}
A()
{
A(0);
}
};
int main(int argc, char* argv[])
{
A object;
cout << object.m_i << endl;
return 0;
}
在 C++中,如果确定了某一个构造函数创建对象,在构造函数中如果调用其他重载的构造函数,它再不会执行其他构造函数的初始化部分代码,而是执行函数体部分的代码。
不会是 0 。因为在默认构造函数中调用一个内部的带参数的构造函数是用户的行为,而不是编译器的行为,就像是调用普通的函数一样,它不会执行构造函数的初始化部分。因此输出结果不会是 0 。
参考资料:
C++ explicit关键字详解