1.构造函数explicit与普通构造函数有什么区别?
(explicit构造函数的作用)
explicit构造函数是用来防止隐式转换的。
代码如下:
class Test1
{
public:
Test1(int n){num=n};//普通构造函数
private:
int num;
};
class Test2
{
public:
explicit Test2(int n){num=n;}//explicit(显式)构造函数
private:
int num;
};
int main()
{
Test1 t1=12;//隐式调用其构造函数,成功
Test2 t2=12;//编译错误,不能隐式调用其构造函数
Test2 t3(12);//显示调用成功
return 0;
Test1的构造函数带一个int型的参数,代码Test1 t1=12;会隐式转换成调用Test1的这个构造函数。而Test2的构造函数被声明为explicit(显式),这表示不能通过隐式转换来调用这个构造函数,因此Test2 t2=12;会出现编译错误。
所以,普通构造函数能够被隐式转换,而explicit构造函数只能显示调用。
2.explicit构造函数的作用
下面的程序f()被调用时,输出是什么?
#include<iostream>
#include<string>
using namespace std;
class Number
{
public:
string type;
Number():type("void"){}
explicit Number(short):type("short");
Number(int):type("int"){}
};
void Show(const Number& n){cout<<n.type;}
void main()
{
short s=42;
Show(s);
}
A.void
B.short
C.int
D.None of above
(1)Show(s)中的s为short类型,其值为42,因此首先检查参数为short的构造函数能否被隐式转换。由于代码第10行的构造函数被声明为显示调用(explicit),因此不能隐式转换。于是进行下一步。
(2)42自动转换为int类型。
(3)检查参数为int的构造函数能否被隐式转换。由于代码第11行参数为int的构造函数没有被声明为显示调用,因此调用此构造函数构造出一个临时对象。
(4)打印上一步临时对象的type成员,即“int”。
3.C++中虚析构函数的作用是什么
大家知道,析构函数时为了在对象不被使用之后释放它的资源,虚函数是为了实现多态。那么,把析构函数声明为virtual有什么作用呢?请看下面的代码:
#include<iostream>
using namespace std;
class Base
{
public:
Base(){};//Base的构造函数
~Base()//Base的析构函数
{
cout<<"Output from the destructor of class Base!"<<endl;
};
void Dosomething()
{
cout<<"Do something in class Base!"<<endl;
};
};
class Derived:public Base
{
public:
Derived() {};//Derived的构造函数
~Derived()//Derived的析构函数
{
cout<<"output from the destructor of class Derived!"<<endl;
};
void Dosomething()
{
cout<<"Do something in class Derived!"<<endl;
};
};
int main()
{
Derived *pTest1=new Derived();//Derived类的指针
pTest1->Dosomething();
delete pTest1;
cout<<endl;
Base *pTest2=new Derived();//Base类的指针
pTest2->Dosomething();
delete pTest2;
return 0;
}
先看程序输出结果:
代码第36行,可以正常释放pTest1资源,而代码第42行没有正常释放pTest2的资源,因为从结果看,Derived的析构函数没有被调用。通常情况下,类的析构函数里面都是释放内存资源的,而析构函数不被调用的话就会造成内存泄漏。原因是指针pTest2是Base类型的指针,释放pTest2时只进行Base类的析构函数。在代码第8行前面加上virtual关键字,由于Base的析构函数时virtual的。就会先找到并执行Derived类的析构函数,然乎执行Base类的析构函数,资源正常释放,避免了内存泄漏。
因此,只有当一个类被用来作为基类的时候,才会把析构函数写成虚函数。
4.看代码写结构——析构函数的执行顺序
#include<iostream.h>
class A
{
private:
int a;
public:
A(int aa){a=aa;};
~A(){cout<<"Destructor A!"<<a<<endl;};
};
class B:public A
{
private:
int b;
public:
B(int aa=0,int bb=0):A(aa) {b=bb;};
~B(){cout<<"Destructor B!"<<b<<endl;};
};
void main()
{
B obj1(5),obj2(6,7);
return;
}
本题考查的是析构函数的执行顺序。析构函数的执行顺序与构造函数的执行顺序相反。
main()函数中定义了两个类B的对象,它们的基类是A。由于这两个对象都是栈中分配的,当main()函数退出时会发生析构,又因为obj1比obj2先声明,所以obj2先析构。它们析构的顺序是首先执行B的析构函数,然后执行A的析构函数。
输出结构如下:
7.复制构造函数是什么?什么是深复制和浅复制
(复制构造函数是什么?什么情况下用到它?什么是深复制什么是浅复制?)
复制构造函数是一种特殊的构造函数。它由编译器调用来完成一些基于同一类的其他对象的构件及初始化。
如果在类中没有显式地声明一个复制构造函数,那么,编译器会私下里制定一个函数来进行对象之间的位复制。这个隐含的复制构造函数简单地关联了所有的类成员。
浅复制是指让新旧两个对象指向同一外部的内容,而深复制是指为新对象制作了外部对象的独立复制。
8.编译器与默认的copy constructor
(什么时候编译器会生成默认的copy constructor?如果已经写了一个构造函数,编译器还会生成copy constructor?)
如果用户没有自定义复制构造函数,并且代码中用到了复制构造函数,那么编译器就会生成默认的复制构造函数;但如果用户定义了复制构造函数,那么编译器就不会生成复制构造函数。
如果用户定义了一个构造函数,且不是复制构造函数,而此时在代码中用到了复制构造函数,那么编译器也还会生成默认的复制构造函数;如果没有使用,则编译器就不会生成默认的复制构造函数。