必知必会问题集锦
第2章:开始学习C++
1.源代码生成可执行代码的过程?
2.编译指令using与using namepace有何区别?
3.cout与cin对应于C中的什么函数,要包含哪个头文件?
4.endl与\n有何区别?
5.main()函数返回的值是什么含义?是返回给程序其他部分还是操作系统?
第3章:处理数据
1.通过cout输出十进制、十六进制和八进制,分别用什么控制符?
2.程序将把1492存储为int、long还是其他整型呢?
3.有原型:ostream& cout.put(char c);那么cout.put('$');与cout << '$';有区别吗?
4.什么是通用字符名?它的用法类似于?通用字符名的表示有哪两种前缀,含义有何不同?后缀表示的那些位是字符的哪个国际标准的码点?例如:ö的国际标准码点为00F6,â的码点为00E2。请➀定义一个变量名为körper的整型变量;➁直接输出字符串:gâteau。
5.ASCII与Unicode与ISO 10646之间的关系?通用字符集(Universal Character Set, UCS)是由ISO制定的哪个标准所定义的标准字符集?
6.扩展字符集用哪个类型来表示?它具体是一个什么类型?8位char不够用所以用扩展字符集吗?它在系统底层表示是unsigned short还是int或者是其他类型呢?cin与cout能直接用在这种宽字符类型吗?宽字符常量和宽字符串,怎么表示?在支持两字节wchar_t的系统中,将会把每个字符存储在一个几字节的内存单元中?
7.char16_t与char32_t表示的是意思分别是?它们分别使用什么前缀去表示字符常量和字符串?与wchar_t一样,它们也都有什么底层类型?
8.bool类型的字面量true和false都可以通过提升转换为int类型吗?可以int ans = true;这样吗?ans等于什么?同样,可以bool start = -100;这样吗?start等于什么?
9.为什么C++提倡使用const代替#define?
10.整型常量和浮点常量的默认类型分别是?
11.static_cast<type>(value)是什么作用?相比C的强转,它有何优点?
12.auto关键字的作用是什么?如下代码中n、x、y分别是什么类型?
auto n = 100;
auto x = 1.5;
auto y = 1.3e12L;
第4章:复合类型
1.如下代码打印出什么?为什么?
int i = 3;
int* p = &i;
char dog[8] = {'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'};
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'};
cout << p;
cout << dog;
cout << cat;
2.如下代码运行,对应的输入,最终打印的结果有什么问题吗?为什么?
代码中cin读取单词后将字符串放到数组中,会自动在结尾添加空字符吗?
int main() {
char name[20];
char dessert[20];
cout << "Enter your name:\n";
cin >> name;
cout << "Enter your favorite dessert:\n";
cin >> dessert;
cout << "I have some delicious " << dessert << " for you, " << name << ".\n";
return 0;
}
Enter your name:
|Alistair Dreeb<Enter>
Enter your favorite dessert:
I have some delicious Dreeb for you, Alistair.
3.以上代码的问题,如何用其他函数来解决?
4.原始(raw)字符串,含义是?格式上来说,前缀是?定界符是?如果字符串中包含定界符,那又该用什么格式呢?如果字符串是wchar_t等类型又该是什么格式呢?
5.什么叫静态联编(static binding)?什么叫动态联编(dynamic binding)?
6.使用数组名或指针时,C++都将执行下面的转换,为什么?arr[-2]代表什么意思?
arrayname[i] => *(arrayname + i)
pointername[i] => *(pointername + i)
7.自动存储、静态存储和动态存储的含义与区别?栈、堆的含义与区别?
8.使变量成为静态有哪两种方式?
9.指针数组是什么意思?声明一个指向指针数组的变量,应该是一个指针的指针吗?指针与指针的指针有何关键区别?
10.模板类vector与array,从功能、底层机制、特点、安全和效率这几个方面对比一下?
第5章:循环和关系表达式
1.下面代码输出什么?为什么?如果要像预想的那样工作,该怎么做?
char word[] = "mate";
bool b = word == "mate";
cout << b;
2.获取时间用哪个函数?要包含哪个头文件?如何得到系统时间的秒数?
3.我们知道cin和scanf一样使用空白(空格、制表符和换行符)来确定字符串的结束位置,如果要读取单个空白符,应该用哪个函数呢?
4.EOF是什么含义?它在源代码中是被怎么定义的?在一个循环读取字符的代码中,如何模拟EOF?此时while循环的条件应该是什么?
5.while(cin)与while(cin.good())、while(!cin.fail())、while(!cin.eof())、while(!cin.bad()),它们有何异同?哪个更通用?
第6章:分支语句和逻辑运算符
1.要判断一个字符是否是字母、空格、数字、标点符号、大小写、控制符…,用哪个函数库?
2.cin.clear()函数的作用机制是什么?如何用它帮助完成输入类型不匹配时给出提示(输入只能是数字而不能是其他字符)?
3.去除输入缓存一般用哪句while循环?
4.C++为标准输入和输出定义了一些格式标志,可通过哪三个函数来控制?
5.写入到文本文件中,一般有哪四个步骤?
6.检测IO错误时,good()、fail()、eof()、bad()它们各有何特点?
第7章:函数——C++的编程模块
1.为何编译器需要函数原型?
2.下图说明了传参的什么特点?

3.对于形参func(int arr[])这里arr实际是:数组or指针?如果将形参改为int* arr可以吗?
4.在形参有何特点时,会用const修饰?目的是?
5.对于一个二维数组arr[r][c],总有恒等式arr==&arr[0]吗?怎么用指针表示法来表示这个二维数组?
6.函数也有指针,那么函数的地址是在哪呢?
7.首先着重理解typedef的妙用,问:下面代码的最后一行代码中的pd代表的意思是?
typedef const double* (*p_fun)(const double*, int);
p_fun pa[3] = { f1,f2,f3 };// f1,f2,f3是函数名
p_fun (*pd)[3] = &pa;
const double* (*(*pd)[3])(const double *, int) = &pa;
第8章:函数探幽
1.简单描述下函数的调用过程?为什么需要内联函数?为什么提倡用内联函数替换宏定义?
2.为什么说引用看上去很像伪装表示的指针?
3.怎么理解如下代码?引用有什么特点?
int & rodents = rats;
实际上是下述代码的伪装表示:
int * const pr = &rats;// 指针常量
4.下图说明了什么?

5.下面代码打印输出什么?
#include <iostream>
using namespace std;
int main() {
int x = 6;
int& y = x;
int& z = y;
y = 9;
cout << x << "," << y << "," << z << endl;
z = 10;
cout << x << "," << y << "," << z << endl;
return 0;
}
6.为什么有时函数要返回引用?返回引用实际上返回的是什么?返回引用一定要注意什么?
7.什么情况下最好将返回类型声明为const引用?
8.如下代码是什么作用?要包含哪个头文件?
cout << numeric_limits<string::size_type>::max() << endl;
9.何时使用引用参数、何时使用指针参数、何时使用按值传递?
10.要设置函数默认值,是要在函数体对应的形参上标明吗?还是说必须得怎样?
11.要为某个参数设置默认值,则必须为它右边的所有参数都提供默认值吗?为什么?
12.使用默认参数,可以减少哪三方面函数的数量?
13.函数模板即是使用泛型来定义函数,那么定义一个函数模板需要在函数体顶部怎么声明?用到哪些关键字?
14.如果函数模板中的类型是个结构体,现要交换结构体的成员,用普通模板函数可以完成吗?如果不能那应该用哪种格式/类型的模板?
15.显式具体化的原型和定义应以什么打头、并通过名称来指出类型?
16.看下面代码,代码中变量xpy是什么类型?怎么解决这种情况?
template<class T1, class T2>
void ft(T1 x, T2 y) {
?type? xpy = x + y;
}
17.下面代码的返回类型怎么解决(注意要用到两个关键字,同时用到后置返回类型)?
template<class T1, class T2>
?type? gt(T1 x, T2 y)
{
return x + y;
}
第9章:内存模型和名称空间
1.为什么要在头文件里面定义#ifndef XXX_H #define XXX_H ..... #endif?
2.cin可以连续获取值吗?比如cin >> screen.x >> screen.y,为什么?
3.下图表达出什么思想(理解图左侧栏1、2、3、4步骤)?

4.“静态”这个词只是说明具有外部链接性。C++为静态存储持续性变量提供了3种链接性,是哪3种?各有什么特点?
5.所有的静态持续变量都有的初始化特征是:未被初始化的静态变量的所有位都被设置为什么值?
6.有4种变量的存储方式,下图很好的总结了,能用自己的话简短再描述一下这4种存储方式吗?

7.C++有“单定义规则”,即变量只能有一次定义,为此,有两种变量声明,一种是定义声明,另一种是引用声明。
那么,引用声明要用哪个关键字,怎么使用?关键字用在哪个文件里?请结合下面代码示例,“在引用变量的同时对它进行赋值会破解引用而成为了定义变量”,这话怎么讲?可以在引用之后再更改引用变量的值吗?
// file1.cpp
int cats = 10;
// main.cpp
#include<iostream>
extern int cats = 20;// Error!去掉赋值“=20”一切OK!
/* 注:赋值后就会为变量分配空间,这就成为了变量定义,就不属于单纯的引用了!*/
int main() {
cats = 30;
std::cout << cats;// 解决掉Error后,就能正常输出:30
}
运行报错:(Error message:“already defined”直观地表明“main.cpp”中引用的变量cats被赋值后就成为变量的定义了)

8.如果一个文件中有全局变量,在一个函数体里也有局部的同名变量,该如何在函数体里访问全局的同名变量呢?
9.下面代码运行会报错吗?它违反了什么规则?
// file1
int errors = 20;
----------------------------
// file2
int errors = 5;
void froobish() {
cout << errors;
}
10.在.cpp文件中全局声明一个const修饰的变量,是“静态,外部链接性”吗?
11.下面代码的上半部分说明了什么问题?是什么原因导致的?代码的下半部分没任何问题,说明了#include的什么本质?
// file1.cpp
const int x = 10;// 放在.cpp文件。链接性:静态,内部
-----------------
// file2.cpp
extern const int x;// 运行后此行报错:unresolved external symbol "int const x"
/* 去掉file1.cpp与file2.cpp中的const,就正常了 */
int main() {
cout << x;
}
############## 上半部分↑↑,下半部分↓↓ ##############
// dolly.h
static int x = 10;// 放在头文件,静态内部链接
const int y = 20; // 放在头文件,静态内部链接
// filey.cpp
#include "dolly.h"// 包含头文件
int main() {
/* 直接【拥有】了头文件的内容,使成为本文件的作用域了!
如果要多此一举,去引用头文件的变量,也是没问题的。*/
cout << x << "," << y; // 正常输出:10,20
}
12.默认函数是什么链接性?加上static关键字后呢?
13.如果要[ 在C++程序中使用C库中预编译的函数 ],将出现什么问题呢?
对于下面第一行代码,假设函数名在C翻译为_spiff,而在C++翻译为_spiff_i,extern "C/C++"必须放置在函数原型吗?它是怎么解决了问题的?
extern "C" void spiff(int);
extern "C++" void spaff(int);// 可缺省前缀,因是C++默认行为
14.如果new找不到请求的内存量,会发生什么?
15.如果想把一个数组或结构放到一个已分配内存的区域,可以用哪种方法?需要包含哪个头文件?
16.使用定位new运算符后,如下面的代码,返回的指针p2、p4该怎么使用?需要配套使用delete吗?为什么?
#include <new>
struct chaff { char dross[20]; int slag; };
char buffer1[50];
char buffer2[500];
int main() {
chaff *p1, *p2;
int *p3, *p4;
p1 = new chaff; // 把结构放进堆中
p3 = new int[20]; // 把int数组放进堆中
// 两种形式的new替换
p2 = new (buffer1) chaff; // 把结构放进buffer1
p4 = new (buffer2) int[20]; // 把int数组放进buffer2
}
17.书上讲到:“在默认情况下,在名称空间中声明的名称的链接性为外部(除非它引用了const常量)。”,怎么理解这句话?下面的示例揭示出的事实怎么与书上讲的是矛盾的呢?结合下面示例,最终能得出什么其他的结论呢?
// my.h
#pragma once
namespace myspace {
int age;
}
// file.cpp
namespace myspaceT {
int ageT;
}
// main.cpp
#include<iostream>
#include "my.h"
int main() {
std::cout << myspace::age;// OK
auto a = myspaceT;// undefined!
auto b = ageT;// undefined!
auto c = myspaceT::ageT;// no class!no namespace!
/* 如果file.cpp中自定义的名称空间默认链接性是“外部”,
* 那main.cpp怎么不能直接访问到呢?根本识别不到那个名称空间的一切。
*/
}

18.using声明和using编译指令有何区别?它们存在的作用是什么?
19.下面代码输出什么?
#include<iostream>
namespace Jill {
double fetch;
}
double fetch;
int main() {
std::cout << &fetch << std::endl;
using Jill::fetch;// using声明!将fetch放入【局部名称空间】main(){}块作用域
std::cin >> fetch;// 读取一个值到Jill::fetch
std::cin >> ::fetch;// 读取一个值到全局fetch
std::cout << fetch << "|" << ::fetch << std::endl;
std::cout << &fetch << "|" << &(::fetch);
}
20.下面代码,第一行是什么?直观意思是?第二行是什么?直观意思又是什么?
using std::cout;
using namespace std;
21.为什么不提倡不分青红皂白地使用using编译指令?举例子说明。
22.未命名名称空间的链接性为?有什么特别的作用?
23.说说名称空间对编程设计蓝图带来的作用?
第10章:对象和类
1.下面代码,直接在类声明外定义私有成员函数,可行吗?为什么?
默认在类中直接给出定义的函数都是什么函数?
// stock.h
class Stock {
private:
long shares;
double share_val;
double total_val;
void set_tot();
public:
......
};
inline void Stock::set_tot() {
total_val = shares * share_val;
}
2.在什么情况下,编译器才会提供默认构造函数?在什么情况下你必须为类提供默认构造函数?
3.有哪两种方式定义默认构造函数?在默认构造函数里通常应该做什么操作?
4.析构函数在什么时候会被程序自动调用?
5.创建的是静态存储类对象、自动存储类对象、通过new创建的对象,这三种情况下,析构函数在何时被自动调用?
6.默认情况下,与给结构赋值一样,在给类对象赋值时(一个对象赋值给另一个对象),会执行什么操作?
7.如下代码,可能会产生临时对象吗?为什么?这时会自动调用谁的析构函数?具体是在什么时候被调用的?
int main() {
Stock stock1("NanoSmart", 12, 20.0);// 直接创建对象
stock1 = Stock("Nifty Foods", 10, 50.0);// 对象赋值(一个匿名对象赋值给一个对象)
...
return 0;
}
8.参考下面代码,问:对象初始化与对象赋值,有什么区别?
Stock stock1("NanoSmart", 12, 20.0);// 对象初始化(语法一)
Stock stock2 = Stock("Boffo Objects", 2, 2.0);// 对象初始化(语法二)
stock2 = stock1;// 对象赋值
9.在上面代码中,stock1与stock2都是自动变量,它们被放入哪块内存?析构的顺序是按什么规则?
10.参考如下代码,可以将列表初始化语法用于类吗?如果可以,初始化的成员要与构造函数的什么匹配?
Stock::Stock(const std::string& cmp, long n = 0, double pr = 0.0);
------------------------------------------------------------------
Stock hot_tip = {"Derivatives Plus Plus", 100, 45.0};
Stock jock {"Sport Age Storage, Inc"};
11.看下面代码,编译器拒绝第2行代码,为什么?怎么解决(如何让成员函数不允许修改[调用对象])?
const Stock land = Stock("Kludgehorn Properties");
land.show();←编译器拒绝
12.应遵守一规则:“只要类方法不修改调用对象,就应该将其他声明为const成员函数”,对吗?
13.每个类都只能有一个析构函数吗?如果构造函数使用了new,则必须提供使用delete的析构函数吗?
14.要比较两个对象的成员,提供一个比较成员函数,如下代码和图,能悟出什么知识点(隐式访问&显示访问)?
const Stock& topval(const Stock& s) const;// 函数原型
假设要对Stock对象stock1和stock2进行比较,并将其中股价总值较高的那一个赋给top对象,则可以使用下面两条语句之一:
top = stock1.topval(stock2);
top = stock2.topval(stock1);

15.看下面代码,引发一个小问题,如何解决?
const Stock& Stock::topval(const Stock& s) const {
if (s.total_val > total_val)// 问:这个大于号后的total_val确定是调用的对象的吗?
return s; // 参数对象
else
return ?????; // 调用的对象
}
16.一般来说,所有类方法都将this指针设置为调用它的对象的地址。所以,以上代码,如下书写,是正确的吗?为什么?
const Stock& Stock::topval(const Stock& s) const {
if (s.total_val > this->total_val)// 其中,“this->”可缺省
return s;
else
return *this;
}
17.this是对象的地址,还是对象本身?
18.对象能创建数组吗?同常规数组相比,用法有什么不一样的吗?
19.看如下代码,花括号中的构造函数创建的是临时对象吗?创建10个元素,结果只初始化了3个,其余的元素默认是什么?这时就必须得为类提供什么构造函数了?
const int STKS = 10;
Stock stocks[STKS] = {
Stock("NanoSmart", 12.5, 10),
Stock();
Stock("Monolithic Obelisks", 130, 3.25)
};
20.如下代码,可行吗?“声明类只是描述了对象的形式,决不能创建类数据成员”,所以,根本原因是什么?
class Bakery {
private:
const int Months = 12;// 本身这句代码是不报错没问题的
double costs[Months];// Error:a nonstatic member reference must be relative to a specific object
21.以上代码的替代方案有哪两种?第一种是?为什么可行?第二种是?为什么可行?
22.以下3种枚举分别有什么不同的含义?
enum {RED, GREEN, BLUE};
--------------------------------------------
enum egg {Small, Medium, Large, Jumbo};
enum t_shirt {Small, Medium, Large, Xlarge};// Error!冲突!因为egg(Small)与t_shirt(Small)位于相同的作用域内。
--------------------------------------------
enum class egg {Small, Medium, Large, Jumbo};
enum class t_shirt {Small, Medium, Large, Xlarge};// OK!避免了上述问题,因为枚举量的作用域为类。
############################################
#include<iostream>
enum egg_old {Small, Medium, Large, Jumbo}; // 无作用域
enum class t_shirt {Small, Medium, Large, XLarge}; // 类作用域
int main() {
egg_old one = Small;
t_shirt rolf = t_shirt::Small;
std::cout << (rolf == t_shirt::Small);// 1
return 0;
}
第11章:使用类
1.运算符重载,是怎么体现OOP思想的?可以看到下面代码,在重载“+”与“*”时使用了不同的参数类型,这也是OK的吗?
Time Time::operator+(const Time& t) const {
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes / 60;
sum.minutes %= 60;
return sum;
}
Time Time::operator*(double mult) const {
Time result;
long totalminutes = hours * mult * 60 + minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
2.重载+ - * / 等运算符后,这些运算符是专门给谁用的?
3.友元有哪三种?C++发明友元,是为了解决什么问题?
4.通过让函数成为类的友元,可以赋予该函数与类的哪个部分具备相同的访问权限?
5.在为类重载几元运算符时(带几个参数的运算符),常常需要友元?
6.请将下面代码转化为成员函数的调用?
A = B * 2.75;
7.能用成员函数重写个重载运算符来解决如下运算吗?
A = 2.75 * B;
-------------
Time Time::operator*(double n, Time& t);// OK or Not?
8.以上代码中的重载运算符成员函数会报错,具体报什么错?对于成员函数重载运算符,对参数个数有什么限定?
9.可以用友元函数重载上述运算符,就没任何问题,那么友元函数最多能带的参数个数有什么限定?
通过如下代码比对成员函数重载和友元函数重载,发现有何异同?这更进一步证明了什么?
// 函数原型
Time operator*(double n) const;
friend Time operator*(double n, Time& t);
-----------------------------------------
// 成员函数
Time Time::operator*(double mult) const {
Time result;
long totalminutes = hours * mult * 60 + minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
// 友元函数
Time operator*(double mult, Time& t) {
Time result;
long totalminutes = t.hours * mult * 60 + t.minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
10.友元函数是非成员函数,它使用的所有值(包括对象)都是显式还是隐式参数?
下面代码中,成员函数的函数体能直接访问私有成员hours、minutes,在友元函数中可以直接访问吗?
/* hours、minutes是Time类的私有成员数据 */
// 成员函数
Time Time::operator*(double mult) const {
long totalminutes = hours * mult * 60 + minutes * mult;
...
}
// 友元函数
Time operator*(double mult, Time& t) {
long totalminutes = t.hours * mult * 60 + t.minutes * mult;
long test = hours + minuts;// 能?or不能?
...
}
11.请将下面代码转化为非成员函数的调用?
A = 2.75 * B;
12.上面友元函数重载的*运算符,它的第一个参数能与第二个参数顺序互换吗?为什么?
13.重载“<<”运算符,必须用友元函数吗?参考如下代码,如果用成员函数重载,会发生什么?
void Time::operator<<(ostream& os);// 假设是成员函数重载<<
-----------------------------------
trip << cout;// 第1个操作数是Time类对象trip,第2个操作数是ostream类对象cout
14.如下代码,如果是“cout << time;”,那么cout是谁的别名?
ostream& operator<<(ostream& os, const Time& t) {
os << t.hours << " hours, " << t.minutes << " minutes";
return os;
}
----------------------------------------
cout << time; <=> operator<<(cout, time);
15.以上代码中,若不是友元函数,t.hours 和 t.minutes是能直接访问到的吗?
那为什么成员函数就能直接通过传进来的参数对象引用直接通过对象名.私有成员访问私有数据成员呢?难道不应该只能通过对象的公有方法访问吗?(提示:对于类的成员函数,函数处于类的内部,因此对于相同类的对象,是可以通过对象名.私有成员来调用的。在一个类的成员函数中可以访问这个类的对象的所有成员(包括私有成员)。)
16.如下代码,接受一个参数(其他参数有默认值的也算)的构造函数,会自动成为对象与参数类型之间的转换函数吗?
为了避免意外的类型转换,C++新增了哪个关键字?是为了防止什么?
Stonewt(double lbs);
--------------------
Stonewt myCat; // 创建一个Stonewt对象
myCat = 19.6; // 使用Stonewt(double)将19.6转换为Stonewt对象
17.转换函数的作用是什么?声明转换函数要注意哪三点?
18.如下代码示例展示了转换函数的用法。你认为转换函数在C++中能发挥出哪些价值?
原则上说,最好使用显式转换,而避免隐式转换。为了防止隐式转换,声明时要加上哪个关键字?
// 函数原型
operator int() const;// 对象转换为int类型的函数
operator double() const;// 对象转换为double类型的函数
------------------------
// 函数实现
Stonewt::operator int() const {
return int(pounds + 0.5);
}
Stonewt::operator double() const {
return pounds;
}
------------------------
// 实际应用
Stonewt poppins(9, 2.8);// 9 stone, 2.8 pounds
double p_wt = poppins;// 使用到转换函数
cout << "Convert to double => Poppins: " << p_wt << " pounds.\n";
/* 使用到转换函数,否则默认int(poppins)==128 */
cout << "Convert to int => Poppins: " << int(poppins) << " pounds.\n";
/*【输出】
Convert to double => Poppins: 128.8 pounds.
Convert to int => Poppins: 129 pounds.
*/
第12章:类和动态内存分配
1.静态类成员有一个特点:无论创建多少对象,程序都只创建一个静态类变量副本。所以用它能起到什么作用?
/*public*/static int HowMany() { return num_strings; }
2.能在类声明中初始化静态成员变量吗?为什么?如果是初始化静态成员常量,是可以的吗?
3.如下代码,为什么要用strcpy函数,而不能直接str = s;?如果非要这样做会有什么具体后果?
StringBad::StringBad(const char* s) {
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
num_strings++;
cout << num_strings << ": \"" << str << "\" object created\n";
}
4.如下代码创建两个构造函数,有什么问题吗?为什么?
Klunk() { klunk_ct = 0; }
Klunk(int n = 0) { klunk_ct = n; }
5.类的复制构造函数的原型的格式是怎样的?它的参数必须得用const修饰吗?为什么?
6.列出五种会自动调用复制构造函数的情况?
7.默认的复制构造函数的作用机理是?
8.如果一些类的数据成员是通过new初始化的,就必须显式地定义什么?如果不这样做会有什么后果?
9.浅复制与深度复制之间的区别是什么?
10.类的赋值运算符的原型的格式是怎样的?
11.赋值运算符的功能是什么?列出一种会自动调用赋值运算符的情况?
12.默认的赋值运算符的作用机理是?
13.在什么情况下需要显式地定义赋值运算符?
14.下面代码展示了显式定义的复制构造函数和赋值运算符,有何异同?
// 复制构造函数
StringBad::StringBad(const StringBad& st) {
num_strings++; // 处理静态成员的更新
len = st.len;
str = new char[len + 1];
std::strcpy(str, st.str);// 复制字符串到新地址
cout << num_strings << ":\"" << str << "\" object created\n";// 打印信息
}
// 赋值运算符
StringBad headline1("Celery Stalks at Midnight");
StringBad knot;// 自动调用默认构造函数初始化knot
knot = headline1;// 赋值运算符被调用
-------------------------------------
StringBad& StringBad::operator=(const StringBad& st) {
/* 赋值操作并不创建新的对象,因此不需要调整静态数据成员num_strings的值。*/
if (this == &st)// 对象赋值给它自己(若不作判断,释放内存操作可能删除对象的内容!)
return *this;
delete[] str;// 释放以前分配的数据(important!)
len = st.len;
str = new char[len + 1];
std::strcpy(str, st.str);
return *this;// 返回一个指向调用对象的引用,这样可以进行连续赋值
}
15.以下前两行代码有什么作用上的区别?后半部分代码为什么能判断是否输入为空行?
str = new char[1];
str = new char;
---------------
char temp[MaxLen];// MaxLen = 81
cin.get(temp, MaxLen);
if (!cin || temp[0] == '\0') {//
// TODO:判断出了输入为空行
}
/* 如果实现遵循了最新的C++标准,则if语句中的第一个条件将检测到空行,第二个条件用于旧版本实现中检测空行。*/
16.(void*)0、NULL、nullptr,都可以表示空指针,那这三者有何区别?建议使用哪个?
17.以下代码编译有问题吗?为什么?
/* String是自定义的类,并且重载了[]运算符 */
const String answer("futitle");
cin >> answer[1];
18.在重载时,C++会区分常量和非常量函数的特征标吗?下面代码中同时存在两个对“[]”的重载有问题吗?
char& String::operator[](int i) {
return str[i];
}
----------------------------------
const char& String::operator[](int i) const {
return str[i];
}
19.在以上重载的基础上,如下代码的后4行有没有问题,如果没问题就说说它使用的是哪个重载的版本?
String text("Once upon a time");
const String answer("futile");
cout << text[1];// ?
cout << answer[1];// ?
cin >> text[1];// ?
cin >> answer[1];// ?
20.静态成员函数可以使用非静态数据成员吗?为什么?
20A.假设类成员的类型为String类或标准string类,如下代码所示,Magazine对象的复制构造函数和赋值运算符的逐成员复制是如何进行的?如果将一个Magazine对象复制或赋值给另一个Magazine对象,为完成深度复制,还需要重载Magazine类的复制构造函数和赋值运算符吗?简单来问,就是对于数据成员类型为类类型(而非基本数据类型)时,数据成员的复制是如何进行的?
class Magazine {
private:
String title;
string publisher;
};
21.函数返回对象将自动调用什么函数?
22.如果函数有两个参数且都是const修饰的,那么如果返回的是对象的引用,也应该声明为const吗?为什么?
23.如下代码返回的是对象而不是引用,正确且合理吗?为什么?
Vector Vector::operator+(const Vector & b) const {
return Vector(x + b.x, y + b.y);
}
24.如果对象是用new创建的dynamic object,则在什么时机下其析构函数才会被调用?
25.如下代码,delete可与定位new运算符配合使用吗?为什么?
指针pc2指向的地址与buffer相同吗?如果非要delete pc2;,那么这是在释放pc2所指向的对象占用的内存吗?
JustTesting(const string& s = "Just Testing", int n = 0) {
words = s;
number = n;
cout << words << " constructed\n";
}
~JustTesting() {
cout << words << " destroyed\n";
}
void Show() const {
cout << words << ", " << number << endl;
}
------------------------------------
int main() {
char* buffer = new char[512];// get a block of memory
JustTesting* pc1, *pc2;
pc1 = new JustTesting("Heap1", 20);// place object on heap
pc2 = new (buffer) JustTesting;// place object in buffer
/* 下行代码是确保在内存单元不重叠,否则会出问题!其中指针pc3相对于pc2的偏移量为JustTesting对象的大小(字节为单位)。*/
// pc3 = new (buffer + sizeof(JustTesting)) JustTesting("Better Idea", 6);
cout << "Address # heap:" << pc1 << " buffer:" << (void*)buffer << endl;
cout << "Content # pc1:@" << pc1 << ": "; pc1->Show();
cout << "Content # pc2:@" << pc2 << ": "; pc2->Show();
delete pc1;// OK
// delete pc2;// 删除pc2指向的对象?NO!会导致运行阶段错误!
delete[] buffer;// free buffer,这完全足够了,because object exists in this buffer!
cout << "Done";
return 0;
}
/*【打印:】
* Heap1 constructed
* Just Testing constructed
* Address # heap:00826B48 buffer:00825EE8
* Content # pc1:@00826B48: Heap1, 20
* Content # pc2:@00825EE8: Just Testing, 0
* Heap1 destroyed
* Done
*/
26.上面代码中,因为调用delete pc1;所以导致对应对象被析构,打印了“Heap1 destroyed”。不能调用delete pc2;的根本原因是什么?没有打印“Just Testing destroyed”即说明并没有调用pc2所指对象的析构函数,那该如何让pc2所指对象的析构函数被调用呢?为什么非要让pc2所指对象调用析构函数呢?
第13章:类继承
1.如下代码,能够使用C-风格字符串初始化std::string对象,根本原因是什么(转换函数?)?
Player(const string& firstname, const string& lastname);
--------------------------------------------------------
Player player1("Chuck", "Blizzard");
2.结合下图,基类的公有成员和私有部分与派生类的关联是怎样的?

3.“创建派生类对象时,程序首先创建基类对象。” 那么,C++使用什么方式来完成这种工作?
4.在基类没有默认构造函数的情况下,如果省略掉对基类的成员初始化列表,会导致什么编译问题?可以在派生类构造函数体中初始化基类对象吗(意义有变吗?)?会导致对象的顺序创建先后问题吗?
5.为什么派生类往往有个如下形式?派生类RatedPlayer构造函数的初始化列表会自动完成对基类[数据成员]的初始化吗?基类TableTennisPlayer并没有定义复制构造函数,这会产生问题吗?
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer& tp) : TableTennisPlayer(tp), rating(r) {}
6.释放对象的顺序与创建对象的顺序相同还是相反?所以首先执行谁的的析构函数?
7.如果函数被声明为virtual,派生类要重写基类的方法,如果函数是通过引用或指针(而不是对象)调用的,它将根据什么来选择调用的函数?如果要声明虚函数,是在基类还是派生类中的方法中使用virtual关键字?如果不声明为虚函数则在调用关系上是什么结果?
8.为什么经常在基类中将派生类会重新定义的方法声明为虚方法?如果基类不声明为虚方法,子类也重写了基类的方法,在通过引用子类的基类引用或指针调用重写的方法时,会调用谁的?这和通过对象(而非引用或指针)调用的结果是一致的吗?
方法在基类中被声明为虚的后,它在派生类中将自动成为虚方法吗?为什么建议在派生类声明中也使用关键字virtual?目的是为了指出什么?
9.在类中声明原型的方法必须有它的实现的代码吗?基类虚方法在派生类中必须重写吗?
10.看下面代码,为什么在子类方法实现中可以直接通过作用域直接调用父类的方法?
class BrassPlus : public Brass {......}
---------------------------------------
void BrassPlus::Withdraw(double amt) {
double bal = Balance();
if (amt <= bal)
Brass::Withdraw(amt);// 如果省略作用域前缀,会引发什么问题?
else if (amt <= bal + maxLoan - owesBank) {
......
Deposit(advance);
Brass::Withdraw(amt);
} else
......
}
11.下面代码,表达出了多态,能自己分析分析吗?
Brass* p_clients[CLIENTS];// CLIENTS = 4
if (...) {
p_clients[i] = new Brass(temp, tempnum, tempbal);
} else {
p_clients[i] = new BrassPlus(temp, tempnum, tempbal, tmax, trate);
}
for (int i = 0; i < CLIENTS; i++) {
p_clients[i]->ViewAcct();// 多态性
}
for (int i = 0; i < CLIENTS; i++) {
delete p_clients[i]; // free memory
}
12.为什么基类需要虚析构函数?如果基类不声明virtual析构函数,会在什么情况下会导致什么问题(参考下面的代码)?
(提示:如果没有virtual则是正常调用基类的函数。)
Brass* b = new BrassPlus(......);
假设Brass没有声明虚析构函数:delete b;只会调用基类的析构函数,而不会调用派生类的析构函数。
如果Brass声明了虚析构函数:delete b;子类BrassPlus对象先被析构,接着调用基类的析构函数。
13.“将基类指针或引用转换为派生类指针或引用——称为向下强制转换(downcasting)。”,那么要如何完成向下强制转换?向下转换非常安全吗?
class Employee {
private:
char name[40];
public:
void show_name();
}
class Singer : public Employee {
public:
void range();
}
--------------------------------
Employee veep;
Singer trala;
Employee* pe = &trala;
Singer* ps = (Singer*) &veep;// 显示类型转换(向下转换)
pe->show_name();
ps->range();// 如不进行显示类型转换,则没有range()方法!
/* 假设设置veep的name字段为"abc",则显示向下类型转换后,下面代码输出什么?*/
ps->show_name();
14.编译器对非虚方法使用[静态or动态]联编?编译器对虚方法使用[静态or动态]联编?
15.参考下图(务必看完且理解之)。虚函数表(virtual function table, VTBL)的概念是?它存储(用什么存储的?)了为类对象进行声明的什么?
在子类及基类中存在着隐藏的指针成员吗?这个指针指向哪?
在子类中重写的基类虚方法的地址与基类的一样吗?在子类中未重写的基类虚方法地址与子类的一样吗?
通过虚函数表原理,详述一下在调用方法时是寻找子类还是基类方法的这个多态性原理(四个步骤)?

15A.如果一个类有虚函数的话,那么这个类的 [对象] 中必须包含虚函数表吗?
15B.根据虚函数表的概念解释一下,下面代码能在main(){}里创建Derived对象吗?如果在Base里给出了对虚方法的定义,情况又是怎样?
(可参考:https://www.zhihu.com/question/24027116/answer/26443605)
class Base {
public:
virtual void go();
};
class Derived : public Base {
public:
void go();
};
16.使用虚函数时,在内存和执行速度方面有一定的成本,包括哪三点?
17.友元可以是虚函数吗?为什么?只有什么函数才能是虚函数?
18.对于没有继承的情况下,protected成员的行为与哪个访问控制相似?在有继承的情况下,protected成员的行为与哪个访问控制相似?
不过,建议:“最好对类数据成员采用私有访问控制,不要使用保护访问控制;同时通过基类方法使派生类能够访问基类数据。”
19.抽象基类(abstract base class, ABC),必须至少包含一个什么函数?如果实现了抽象基类的所有纯虚函数则能创建抽象基类的实例对象吗?在派生类中必须定义抽象基类的纯虚函数吗?如果不定义则派生类会成为什么类型的类?

20.上面代码中抽象基类ABCBase给出对纯虚函数的定义好像没有任何意义吧?毕竟抽象类本身不能被实例化,它只能通过指针/引用指向/引用派生类对象,既然存在派生类对象,也就是说派生类100%重写了抽象类的纯虚函数,所以根本没有任何途径(比如通过抽象基类指针)能调用到抽象类的纯虚函数,要调用肯定是根据多态调用到派生类重写过的同名函数,不过对于其他任何非纯虚公有函数因被派生类继承的原因倒是都可以访问到。
问:纯虚函数与纯虚析构函数有什么区别?(提示:1.在抽象基类给不给定义;2.在派生关系中的析构函数执行过程。从这两方面去想)
21.“可以将ABC看作是一种必须实施的接口。ABC要求具体派生类覆盖其纯虚函数——迫使派生类遵循ABC设置的接口规则。” 这使得组件设计人员能够制定什么?以确保从ABC派生的所有组件都至少支持ABC指定的功能。
22.结合下面的代码(基类和派生类都使用了new),问:为什么[必须]定义显示析构函数、复制构造函数和赋值运算符?
// Base Class Using DMA
class baseDMA {
private:
char * label;
......
};
// derived class Using DMA
class hasDMA : public baseDMA {
private:
char * style;
......
}
--------------------------------
// 析构函数(如果不定义,后果怎样?)
baseDMA::~baseDMA() {
delete [] label;
}
hasDMA::~hasDMA() {
delete [] style;
}
--------------------------------
// 复制构造函数(如果不定义,后果怎样?)
baseDMA::baseDMA(const baseDMA & rs) {
label = new char[std::strlen(rs.label) + 1];
std::strcpy(label, rs.label);
rating = rs.rating;
}
hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs) {// 为什么能以及要通过初始化列表调用基类复制构造函数?
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
}
--------------------------------
// 赋值运算符(如果不定义,后果怎样?)
baseDMA & baseDMA::operator=(const baseDMA & rs) {
if (this == &rs)
return *this;
delete [] label;
label = new char[std::strlen(rs.label) + 1];
std::strcpy(label, rs.label);
rating = rs.rating;
return *this;
}
hasDMA & hasDMA::operator=(const hasDMA & hs) {
if (this == &hs)
return *this;
/* 使用运算符(*this = hs;)表示法会因为赋值运算符被重载从而会而形成递归调用!
所以,此处必须使用函数表示法!如果不加baseDMA::前缀,也是本函数的递归调用!*/
baseDMA::operator=(hs); // 赋值基类部分
delete [] style;// 释放old,为新style作准备
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
return *this;
}
23.结合下面的代码(基类有友元),问:派生类如何访问基类的友元?因为友元不是成员函数,所以不能使用什么方式来指出要使用哪个函数?
class baseDMA {
public:
friend std::ostream& operator<<(std::ostream& os, const baseDMA& rs);
......
}
class lacksDMA :public baseDMA {
public:
friend std::ostream& operator<<(std::ostream& os, const lacksDMA& rs);
......
}
class hasDMA :public baseDMA {
public:
friend std::ostream& operator<<(std::ostream& os, const hasDMA& rs);
......
}
--------------------------------------------
std::ostream& operator<<(std::ostream& os, const baseDMA& rs) {
os << "Label: " << rs.label << std::endl;
os << "Rating: " << rs.rating << std::endl;
return os;
}
std::ostream& operator<<(std::ostream& os, const lacksDMA& ls) {
os << (const baseDMA&)ls;// 强制类型转换,以使用基类的<<运算符
/* 以上,好像直接通过os = operator<<(os, ls);调用也是OK的?*/
os << "Color: " << ls.color << std::endl;
return os;
}
std::ostream& operator<<(std::ostream& os, const hasDMA& hs) {
os << (const baseDMA&)hs;// 强制类型转换,以使用基类的<<运算符
os << "Style: " << hs.style << std::endl;
return os;
}
/* 注:更佳的强制类型转换方式是:dynamic_cast<const baseDMA&>(hs) */
24.如果定义了某种构造函数,编译器将不会定义什么函数?在这种情况下,如果需要,则必须?
25.在哪四种情况下,将使用复制构造函数?
26.下面代码中,理解初始化与赋值的区别。第2行是初始化or赋值?第4行是初始化or赋值?
1. Star sirius;
2. Star alpha = sirius;// 初始化or赋值?
3. Star dogstar;
4. dogstar = sirius;// 初始化or赋值?
27.编译器不会生成将一种类型赋给另一种类型的赋值运算符。如果希望能够将字符串(const char*)赋给Star对象,则方法之一是显式定义什么运算符?具体的代码是?另一种方式是使用?具体的代码是?如果将Star对象转换为字符串,如何实现?
28.派生类继承了基类的构造函数吗?即:在main(){}中可以通过派生类对象直接调用基类的构造函数吗?
29.对于基类,即使它不需要析构函数,也应提供一个什么(虚)函数?
30.按值传递对象(传参/返回对象等)涉及到生成临时拷贝,即调用复制构造函数,然后调用?
31.返回const引用/指针,用来确保返回的对象不能被?
32.如果方法返回对this或stock的引用,且this和stock都被声明为const,所以函数不能对它们进行修改,这意味着返回的引用也[必须]被声明为?
33.派生类引用不能自动引用基类对象,因为下面代码不能运行。如果写一个什么构造函数或显示定义个什么,就能完成功能?其实还有一种方式是?
Brass gp("Griff Hexbait", 21234, 1200);// 基类
BrassPlus temp;// 派生类
temp = gp;// 不能运行!
---------------------
BrassPlus(const Brass&);// 以上可以运行了!
BrassPlus& BrassPlus::operator=(const Brass&);// 以上可以运行了!
如果以上都没定义,则不能运行,除非使用显式强制类型转换!
34.以下代码中,ViewAcct()方法是调用的Brass还是BrassPlus对象的?
void inadequate(Brass ba) {
ba.ViewAcct();
}
----------------------------
BrassPlus buzz("Buzz Parsec", 00001111, 4300);
inadequate(buzz);
35.请着重逐个理解下表中的每项,请用自己的语言描述说说?
(注:new/delete的重载默认是static的,static关键字可缺省)

第14章:C++中的代码重用
1.下面代码是建立了一个类的什么关系?具体体现在数据成员的不同在哪?
#include <iostream>
#include <string>
#include <valarray>
class Student {
private:
typedef std::valarray<double> ArrayDb;
int identifier;
std::string name;// contained object
ArrayDb scores;// contained object
...
};
2.看下面的初始化列表,name和scores成员的初始化是通过调用什么完成的?
Student(const char * str, const double * pd, int n)
: name(str), scores(pd, n) {}
3.成员列表初始化顺序是怎样的?需要有严格的逻辑上的排列顺序吗?
4.私有继承后,基类的公有和保护成员都将成为派生类的私有成员,那派生类连基类公有方法都不能访问,也就是不能访问基类任何数据了,不就没意义了吗?问:私有继承是[先]使基类公有和保护成员变私有、[再]继承已全部私有化的成员、[最后]基类成员都成为派生类的私有成员吗?问题中的“[先]”这个步骤有问题吗(关键在于是从哪开始变为私有权限的)?正确的顺序理解是怎样的?
5.比对下面两种实现包含(has-a)关系的代码,它们在设计和使用上有什么具体区别?
// 常规包含(has-a)
class Student {
private:
typedef std::valarray<double> ArrayDb;
std::string name;// contained object
ArrayDb scores;// contained object
public:
Student(const char* str, const double* pd, int n) : name(str), scores(pd, n) {}
double Average() const {
if (scores.size() > 0) return scores.sum() / scores.size();
else return 0;
}
const string& Name() const { return name; }
}
// 私有继承(has-a)
class Student : private std::string, private std::valarray<double> {
private:
typedef std::valarray<double> ArrayDb;
public:
Student(const char* str, const double* pd, int n) : std::string(str), ArrayDb(pd, n) {}
double Average() const {
/* 使用私有继承时,该ArrayDb对象没有名称,所以使用类名和作用域解析运算符来调用公有方法。 */
if (ArrayDb::size() > 0) return ArrayDb::sum() / ArrayDb::size();
else return 0;
}
/* 使用私有继承时,该string对象没有名称,所以使用强制类型转换。 */
const string& Name() const { return (const string &) *this; }
}
6.对于私有继承,可通过使用类名和作用域解析运算符来调用基类公有方法,但友元不属于类,那么在派生类的友元函数里面怎么调用基类的方法呢?
7.由于既可以使用包含,也可以使用私有继承来建立has-a关系,那么应使用种方式呢?
8.在哪[两种]情况下(常规组合/包含无法办到),应该使用私有继承?
9.使用保护继承时,基类公有/保护成员将成为派生类的保护成员,但在继承层次结构之外是不可用的(比如类对象在main(){}中是不能调用保护成员的)。那么,保护继承和私有继承有啥区别,因为具体使用时的访问权限完全一样。问:保护继承与私有继承在单一继承结构里对基类的访问权限是一样的吗?如果有第三代继承,权限又有什么变化?
10.下面任何没有问题,说明第N代公有或保护继承后,第N代可[直接]访问[基类]的什么成员?
class Animal {
protected:
int A;
};
class Wolf : protected Animal {};
class Dog : protected Wolf {
protected:
void test() { int y = A; }// can vist base class protected member [directly].
};
11.隐式[向上转换]即是基类指针/引用能直接使用派生类指针/引用而无需任何显式的转换操作。
问:➀公有继承能隐式向上转换吗?➁保护继承能隐式向上转换吗?有何限制(在main(){}中可以吗?)?➂私有继承能隐式向上转换吗?如果不能则说明在私有继承里,基类指针/引用不能直接指向/引用派生类对象,那该如何完成同样的功能呢?
// 基类Brass,派生类BrassPlus
void BrassPlus::Setplus(const Brass&);// 派生类方法
BrassPlus bp;// 派生类对象
公有继承、保护继承、私有继承,分别在这3种情况下,pb能作为Setplus函数的参数吗?
-------------------------------
// 情形:BrassPlus保护继承Brass
void BrassPlus::test() {// 对类函数原型的定义(在类作用域)
Brass* b = new BrassPlus;// 能or不能?
}
int main() {// 不在类作用域
Brass* b = new BrassPlus;// 能or不能?
}
// 情形:BrassPlus私有继承std::string
void BrassPlus::test() {// 对类函数原型的定义(在类作用域)
std::string* s = new BrassPlus;// 能or不能?
}

12.使用哪个关键字可以重新定义访问权限?比如私有继承中派生类想直接在main(){}中通过对象直接访问基类的公有方法。
class Student : private std::valarray<double> {
private or public?:
[keyword] std::valarray<double>::min;
[keyword] std::valarray<double>::operator[];
...
}
--------------------------------
int main() {
Student stu;
/* 虽然[]和max()在std::valarray<double>类中是公有的,但对于Student的实例对象,[]和max()是私有的。*/
/* 这里Student对象能直接访问Student类的私有成员,原因只有一个,就是访问权限被修改了(重新定义了)。*/
double m = stu[i].max();
}
13.对于多重继承MI,下面的代码为何存在二义性问题?然后请回答代码中的提问。

SingingWaiter ed;
Worker* pw = &ed;// ambiguous, lead to compiler error!
------------------------------
SingingWaiter sw;
sw.setFullname("Jim Green");// 问:假设Worker类有个公有且有定义的setFullname方法,存在二义性吗?
------------------------------
// 假设go()是Worker类独有(未被重写)的公有成员函数。问:下面两个对go()方法的调用产生的结果是一样的吗?
Worker * pw1 = (Waiter *) &ed; pw1.go();
Worker * pw2 = (Singer *) &ed; pw2.go();
14.对于下面的类层次结构,在Worker类中申明一个[纯]虚析构函数是为了起到什么作用?这个基类的纯虚析构函数必须要有定义吗?如果非不定义,则在什么情况下产生什么后果?
class Worker {
public:
virtual ~Worker() = 0;
}
class Waiter : public Worker {}
class Singer : public Worker {}
15.虚基类是个什么概念?它的作用是什么?如何在派生关系中让一个基类成为虚基类?
16.假设有父子孙多重继承,在一般情况下,孙类能直接在初始化列表里面初始化父类吗?为什么?
现在如果子类是通过虚基类继承父类的,这种情况下,孙类必须完成对父类的初始化吗?为什么?
虚基类使得从多个类(它们的基类相同)派生出的对象只继承一个基类对象,问:这个唯一的基类对象是由谁初始化的?
17.假设如下代码没有任何问题(其实是有错误存在的),运行程序后,创建了一个C类对象,问:初始化过程中初始化了类A的对象吗?即,类A的成员a现在等于123吗?
因为代码中其实存在问题,那么问题在哪?怎么解决?说明虚基类不能由谁去初始化、又必须由谁去初始化?
(事实证明:虚基类会忽略(阻断)它的直接子类给它传递的任何数据。)
class A {
private:
int a;
public:
A(int n) : a(n) {}
void show() { cout << "a=" << a << endl; }
};
class B : virtual public A {
public:
B(int n) : A(n) {}
};
class C : public B {
public:
C(int n) : B(n) {}// 编译有错误:“no default constructor exists for class "A"”
};
int main() {
C c = C(123);
c.show();
return 0;
};
--------------------
如果虚基类继承改为普通继承,程序也将没任何报错,明显运行程序打印“a=123”。
解决编译错误,在类C中加上对类A的初始化:
类C的构造函数改正为:C(int n) : A(9) : B(n) {}
问:运行程序最终打印“a=123”还是“a=9”?
18.下面代码有注释的那行代码,调用的是类B还是类C的公有成员函数“go()”呢?
对于这种二义性错误,如果要指哪个就调用哪个,则语法格式是怎样的?
class A {};
class B : virtual public A {
public:
void Show() const { cout << "B go" << endl; }
};
class C : virtual public A {
public:
void Show() const { cout << "C go" << endl; }
};
class D : public B, public C { }
int main() {
D d;
d.Show();// invoke whose function?
return 0;
}
19.对于以上问题,从类设计的角度,还有什么好的解决方案?
void A::Show() const {
cout << "Name:" << fullname << endl;
cout << "Employee ID:" << id << endl;
}
void B::Show() const {
A::Show();
cout << "Panache rating:" << panache << endl;
}
void C::Show() const {
A::Show();
cout << "Presence rating:" << presence << endl;
}
----------------------------------------------------
void D::Show() {
B::Show();
C::Show();
}
2987

被折叠的 条评论
为什么被折叠?



