1. 什么是构造函数?构造函数有返回值吗?构造函数如何命名?构造函数可以重载吗?什么是缺省构造函数?什么情况下,类中会有缺省构造函数?
构造函数是一种特殊的函数,用来在对象实例化的时候初始化对象的成员变量
构造函数没有返回值
A constructor is a method whose name is the same as the class name.
构造函数必须与类名相同
构造函数可以进行重载,每个构造函数必须有不同的函数签名
class Date { int d, m, y; public: Date(int dd, int mm, int yy); Date(int dd, int mm); // today's year Date(int dd); // today's month and year Date(); // default Date: today Date(const char* p); // date in string representation /* ... */ };The default constructor is a constructor that can be invoked with no arguments.
缺省构造函数($default$ $constructor$): 缺省构造函数是可以不带参数调用的构造函数。一个类中,只能有一个缺省构造函数。
The compiler provides a public default constructor for a class with two exception:
- If a class explicitly declare any constructor, the complier does not provide a public default constructor. In this case, the programmer must provide a public default constructor if desired.
- If a class declares a nonpublic default constructor, the complier does not provide a public default constructor.
当没有定义构造函数或者定义的构造函数没有参数时,类中会有缺省构造函数。
2. 构造函数的作用是什么?什么时候会被调用?构造函数的执行顺序是什么?
(父类与子类的构造函数、类自身与其数据成员的构造函数)
- 作用: 用来在对象实例化的时候初始化对象的成员变量
- 调用时间: 当类被创建时,自动调用构造函数
- 执行顺序: 先执行父类的构造函数,再执行数据成员的初始化(成员中有类,执行该类的构造函数),最后执行子类的构造函数
3. 为什么拷贝构造函数(copy constructor)的参数必须是按引用传递(by reference)而不能是按值传递(by value)?
class Base
{
public:
Base(){}
Base(const Base b){
//..
}
};
Base A;
Base B=A;
- 值传递不可以原因: 以上述代码说明,当$B$需要调用其拷贝构造函数,需要将$A$进行值传递进入$B$的拷贝构造函数,而在进行值传递时,$B$的拷贝构造函数会生成一个该类的临时对象(假设为$C$),会执行
Base C=A(初始化形参,也就是初始化函数的局部变量),这又将调用$C$的拷贝构造函数,将$A$以值传递的方式传入,如此往复,每一次都会产生新的对象,无限递归调用拷贝构造函数从而耗尽资源,产生错误.
class Base
{
public:
Base(){}
Base(const Base& b){
//..
}
};
Base A;
Base B=A;
- 引用传递可以的原因:当上述$B$调用拷贝构造函数时,会生成一个临时的引用变量,而不是对象,会执行
Base &C=A(同样是初始化形参,只不过是初始化一个引用),这不会产生新的对象,也就不会调用拷贝构造函数,只是增加了一个指向A的临时引用而已。
4. 拷贝构造函数(复制构造函数)的作用是什么?什么是浅拷贝?什么是深拷贝?
- 拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
- 作用: 用来复制对象,在使用这个对象的实例来初始化这个对象的一个新的实例。
- 浅拷贝和深拷贝: 拷贝者和被拷贝者若是同一个地址,则为浅拷贝,反之为深拷贝,深拷贝会在堆内存中另外申请空间来储存数据。默认的拷贝构造函数实现的是浅拷贝,数据成员中有指针时,必须要用深拷贝。
//深度拷贝
int a = 5;
int b = a;
//浅拷贝
int a = 8;
int *p;
p = &a;
char* str1 = "HelloWorld";
char* str2 = str1;
5. 全局对象的构造函数、析构函数分别是什么时候被调用的?自动局部对象的构造函数、析构函数分别是什么时候被调用的?静态局部对象的构造函数、析构函数分别是什么时候被调用的?
- 全局对象($Global$ $scope$ $objects$)的构造函数在程序运行前被调用,析构函数在程序结束前最后被调用;
- 自动局部对象($Automatic$ $local$ $objects$)的构造函数在程序执行到对象定义时自动被调用,析构函数在对象离开范围时(离开定义对象的块时)被调用;
- 静态局部对象($static$ $local$ $objects$)的构造函数在程序执行到对象定义时自动被调用,析构函数在程序结束前被调用。
6. 什么是初始化列表?它的作用是什么?
(提示:一般数据成员的初始化、常成员的初始化,对象成员构选函数的选择、父类构造函数的选等)
- 初始化列表($Initialization$ $Sections$):与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。
class foo{
public:
// 初始化列表
foo(string s, int i): name(s), id(i){} ;
private:
string name ;
int id ;
};
- 作用:
- 初始化一般数据成员(如上例)
- 初始化常量成员(原因:常量只能初始化不能赋值,所以必须放在初始化列表中)
- 初始化引用类型(原因:引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面)
- 初始化没有缺省构造函数的类类型(原因:使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化)
举例:类A的对象a是类B的数据成员
#include <iostream>
using namespace std;
class A {
int i;
public:
A(int );
};
A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}
// Class B contains object of A
class B {
A a;
public:
B(int );
};
B::B(int x):a(x) { //Initializer list must be used
cout << "B's Constructor called";
}
int main() {
B obj(10);
return 0;
}
/* OUTPUT:
A's Constructor called: Value of i: 10
B's Constructor called
*/
- 初始化基类成员(父类成员)
#include <iostream>
using namespace std;
class A {
int i;
public:
A(int );
};
A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}
// Class B is derived from A
class B: A {
public:
B(int );
};
B::B(int x):A(x) { //Initializer list must be used
cout << "B's Constructor called";
}
int main() {
B obj(10);
return 0;
}
- 当构造函数的参数名称与数据成员相同,必须使用this指针或初始化列表初始化数据成员。
#include <iostream>
using namespace std;
class A {
int i;
public:
A(int );
int getI() const { return i; }
};
A::A(int i):i(i) { } // Either Initializer list or this pointer must be used
/* The above constructor can also be written as
A::A(int i) {
this->i = i;
}
*/
int main() {
A a(10);
cout<<a.getI();
return 0;
}
/* OUTPUT:
10
*/
7. 什么是析构函数?析构函数有返回值吗?析构函数如何命名?析构函数可以重载吗?
- 析构函数也是一个在类中跟构造函数类似的特殊功能的成员函数,作用与构造函数相反,是在对象的声明周期结束的时候会被自动调用。
- 析构函数没有返回值
- 在C++中析构函数的名字跟类名相同,并在前面带上一个取反的符号
~,表达的意思也跟构造函数的过程相反Because there is no parameter in a destructor, the destructor can not be overloaded. That is, a class has only one destructor.
- 析构函数不可以重载,析构函数在类中仅有一个
8. 析构函数的作用是什么?什么时候会被调用?为什么析构函数通常是虚函数,如果不是虚函数,会如何?
- 作用: 清空并释放对象先前创建或者占用的内存资源
- 调用时间: 析构函数对象消亡时自动被调用
- 原因: 如果析构函数不被声明成虚函数,则编译器采用的绑定方式是静态绑定,在删除基类指针时,只会调用基类析构函数,而不调用派生类析构函数,这样就会导致基类指针指向的派生类对象析构不完全。
- 如何: 若是将析构函数声明为虚函数,不管派生类的析构函数前是否加$virtual$(可以理解为编译器优化),都构成重写。基类的指针指向派生类的对象,然后调用重写虚函数——析构函数,构成了多态,而多态与类型无关,只与对象有关,所以就能够调用的就是派生类的析构函数了。
9. 如果要编写一段程序,跟踪类A所创建的实例的个数,请叙述编写程序的大体思路。
使用静态数据成员,构造函数时$+1$,析构函数时$-1$.
class A {
public:
A( )
{
++count;
//…
}
~A( )
{ - -count;
//…
}
private:
static unsigned count; // class data member
};
unsigned A:: count = 0;
10. 什么是C++中的三大函数(The Big Three)?
- 析构函数:
~S() - 拷贝构造函数:
S(const S& s) - 赋值函数:
operator=