总结2
1. In C++, what does "explicit" mean?
答:explicit用在构造器上,防止某些隐式转换。比如下面的例子:
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
class CLS
{
public:
string str;
explicit CLS(string s) :str(s) {}
};
void printStr(CLS obj) {
cout << obj.str << endl;
}
void main(void)
{
string s = "Hello world";
//printStr(s);使用explicit后就不能用了
}
2. 下面代码的打印结果是什么?
#include <iostream>
using std::cout;
using std::endl;
class CLS
{
public:
static int num;
CLS() {
num++;
}
~CLS() {
num--;
print();
}
void print() {
cout << "num: " << num << endl;
}
};
int CLS::num = 0;
CLS f(CLS x) {
//x.print();
return x;
}
void main(void)
{
CLS c1;
c1.print();
CLS c2 = f(c1);
c2.print();
cout << "----" << endl;
return;
}
答:具体的打印结果如下:
num: 1
num: 0
num: 0
----
num: -1
num: -2
这里主要考察的是拷贝构造函数和析构函数,普通构造函数也考了,不过不是重点。
首先要说明拷贝构造函数发生在哪些情况下:1)使用一个类初始化另一个类时;2)将类作为值参数进行传递时;3)将类作为值返回时。
本题就符合上述的123三种情况;因此在f()函数中发生2次拷贝构造函数的调用,又因为在函数当中,中间的1个临时变量在函数结束后就消失了,因此还会调用析构函数,因此会有1次打印;而第3次就是c2.print()。有一点比较奇怪,这里的类初始化和值返回都出现了,但是只有一次拷贝。
上述结果中,第1次打印来自c1.print();第2次打印是析构函数的调用;第三次打印是c2.print();最后两次是因为main函数结束后,需要释放c1和c2,所以还要调用两次析构。
上面的代码还可以修改一下,来考察赋值构造函数和拷贝构造函数:
#include <iostream>
using std::cout;
using std::endl;
class CLS
{
public:
static int num;
CLS() {
num++;
}
CLS(CLS &c) {
cout << "copy" << endl;
num = c.num;
}
CLS & operator=(CLS &cls) {
cout << "operator" << endl;
return *this;
}
~CLS() {
num--;
print();
}
void print() {
cout << "num: " << num << endl;
}
};
int CLS::num = 0;
CLS f(CLS x) {
//x.print();
return x;
}
void main(void)
{
CLS c1;
c1.print();
CLS c2;
c2= f(c1);
c2.print();
cout << "----" << endl;
return;
}
打印的结果如下:
num: 1
copy
copy
num: 1
operator
num: 0
num: 0
----
num: -1
num: -2
3. 在c++编程中,传值方式和传引用方式之间有什么区别?
答:二者的区别为,当传引用时,实际传到函数中的形参,是实际参数的一个引用,而不是仅传递值到函数中。具体的表现有以下几条:
1 传引用时,形参和实参是同一个变量,即使用相同的内存空间,二者有相同的地址。而传值时二者地址不同;
2 传引用时,由于没有新建变量,所以对于类对象参数,不会产生构造和析构。而如果是传值调用,调用时会进行构造,退出函数时会进行析构;
3 由于传引用使用的是原本实参的地址,所以对引用参数值的修改,会在退出函数后体现在主调函数中,而传值调用对参数的修改不会影响到主调函数。
4. 下面代码中编译会报错的是哪个:
struct CLS
{
CLS() {}
CLS(int i) {}
void func() {}
};
void main(void)
{
CLS a(1); //A
a.func(); //B
CLS b(); //C
b.func(); //D
}
答:虽然实际的错误来自C,但是编译报错的部分是D。C可以看成是一个返回值是CLS的函数b,所以编译并不会报错。
5. 下面代码的打印结果:
#include <iostream>
using std::cout;
using std::endl;
class CLS
{
public:
int i;
int j;
CLS(int a) :j(a), i(j) {}
};
void main(void)
{
CLS a(1);
cout << a.i << " " << a.j << endl;
}
答:xx 1,xx表示不能确定。
这里的考察点是成员变量的初始化顺序,它依赖于类定义中成员的声明顺序,而不是初始化列表中的顺序。因此上例中,i需要先被j初始化,而j的值还是随机的;然后才是j被初始化为1。
6. 构造函数为什么不能是virtual的?
答:virtual意味着在执行时期进行绑定,所以在编译时需确定信息的不能为virtual。构造函数需在编译时确定,因为需构造出个对象,才能执行动作。
virtual意味着派生类可以改写其动作,派生类的构造函数会先执行基类的构造函数而不是取代基类构造函数,也就是说基类的构造函数可以看作派生类构造函数的组成,所以并不能改写这个函数。
7. 如果类的构造函数调用自身,结果会怎么样?
答:看下面的例子:
class CLS {
public:
CLS(int i) {
CLS(1);
}
private:
int number;
};
int main() {
CLS *c = new CLS(1);
return 0;
}
编译的时候会报告警:
1>------ 已启动生成: 项目: CppTest, 配置: Debug Win32 ------
1> main.cpp
1>e:\code\c++\cpptest\cpptest\main.cpp(9): warning C4717: “CLS::CLS”: 如递归所有控件路径,函数将导致运行时堆栈溢出
1> CppTest.vcxproj -> E:\code\C++\CppTest\Debug\CppTest.exe
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
如果运行的话,就直接异常。
8. 函数func的参数是几个:
func((a, b), (c, d, e), f);
答:3个。
下面是一个例子:
#include <stdio.h>
void func(int x, int y, int z) {
printf("%d ", x);
printf("%d ", y);
printf("%d ", z);
}
int main(void) {
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
func((a, b), (c, d, e), f);
}
打印的结果如下:
2 5 6
这里考察的点其实是逗号运算符吧。(a, b)这里就返回b,(c, d, e)就返回e,因此结果就是2 5 6。