0. 知识点
1. 类中的成员可以用public/protected/private分别进行修饰,这三种成员在什么情况下是可以被访问的?
类中没有用public/protected/private修饰的成员,其可访问性是什么,结构体中没有用public/protected/private修饰的成员,其可访问性是什么?
见$OOP5$面向对象概论问题$3$,此处为了方便将其复制过来
- $public$: 修饰的成员可以在任何地方被访问
- $private$: 修饰的成员只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类对象也不能访问
- protected: 修饰的成员可以被该类中函数、子类函数、友元函数访问;但不能被该类对象访问
- 在类中没有用$public/protected/private$修饰的成员,其可访问性默认是$private$
- 在结构体中没有用$public/protected/private$修饰的成员,其可访问性默认是$public$
2. 什么是封装?其作用是什么?
见$OOP4$面向对象概论问题$2$,此处为了方便将其复制过来
封装: 封装是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的函数代码进行有机结合,形成类。
作用: 使一部分成员充当类与外部的接口,而将其他成员隐藏起来,这样就达到了对成员访问权限的合理控制,使不同类之间的相互影响减少到最低限度,进而保护数据增强数据的安全性和简化程序编写工作。
3. 若父类中没有缺省构造函数,则对派生类的构造函数有什么要求?
- 要求: 派生类含有构造函数,且要对父类通过初始化列表进行构造.
- 原因: 父类中没有缺省构造函数,假设派生类无构造函数(即只有编译器提供的缺省构造函数),那么就不会先自动构造父类的构造函数,这是不允许的。
4. 构造函数的作用是什么?什么时候会被调用?构造函数的执行顺序是什么?
(父类与子类的构造函数、类自身与其数据成员的构造函数)
见$OOP7$面向对象概论问题$2$,此处为了方便将其复制过来
- 作用: 用来在对象实例化的时候初始化对象的成员变量
- 调用时间: 当类被创建时,自动调用构造函数
- 执行顺序: 先执行父类的构造函数,再执行数据成员的初始化(成员中有类,执行该类的构造函数),最后执行子类的构造函数
5. 什么是初始化列表(Initialization Sections)?它的作用是什么?
(提示:一般数据成员的初始化、常成员的初始化,对象成员构选函数的选择、父类构造函数的选等)
见$OOP7$面向对象概论问题$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
*/
6. 如何防止一个头文件被多重包含?举例说明。
见$OOP1-003$面向对象概论问题$8$,此处为了方便将其复制过来
使用条件编译
假设包含的文件名为x.h
,写成如下:
#ifndef _x_h
#define _x_h
//...其他内容
#endif
7. 父类成员中的public、protected、private成员,哪些在子类中是可以访问的?在公有继承、私有继承、受保护继承三种继承方式下,父类成员中的public、protected、private成员被继承到子类后,其可访问性分别是什么?派生类是否可以继承父类的构造函数和析构函数?
- 父类的$public$、$protected$可以在子类中进行访问,而父类的$private$不可以在子类中访问
- 对外: (顺序为$public$、$protected$、$private$)
公有继承(透明): $public$ 、$protected$ 、 $private$
受保护继承(半透明): $protected$ 、$protected$ 、$private$
私有继承(不透明): $private$ 、$private$ 、 $private$
- 派生类不可以继承父类的构造函数和析构函数
8. 多重继承会带来什么问题?在C++中是如何解决的?
class BC0 {
public: int K;
};
class BC1 : public BC0 {
public: int x;
};
class BC2 : public BC0 {
public: int x;
};
class DC : public BC1, public BC2{
};
- 问题$1$: 派生类中的对象中存在多个同名成员问题,该怎么解决?
类DC的对象中存在多个同名成员 x, 应如何使用?
- 解决方案:
d.BC1::x = 11; // from BC1
d.BC2::x = 12; // from BC2
- 问题$2$: 派生类的对象中存在来自同一个类的成员,该如何区分?
类DC的对象中,存在两份来自类BC0的成员K,如何区分?
- 解决方案: 采用虚继承的方式解决
class BC0 {
public: int K;
};
class BC1 : virtual public BC0 {
public: int x;
};
class BC2 : virtual public BC0 {
public: int x;
};
class DC : public BC1, public BC2{
public: int x;
};
// 虚继承使得BC0仅被DC间接
// 继承一份
d.BC0::K = 13; // O.K.