1、动态绑定
c++中当使用基类的引用或指针调用一个虚函数时将发生动态绑定。
2、虚函数继承
如果基类把一个函数声明成虚函数,则该函数再派生类中隐式地也是虚函数。
3、派生类构造函数
尽管在派生类对象中含有从基类继承而来的成员,但是派生类并不能直接初始化这些成员。和其他创建了基类对象的代码一样,派生类也必须使用基类的构造函数来初始化它的基类部分。每个类控制它自己的成员初始化过程。
class A
{
public:
A() :a(0) { cout << "A cons" << endl; }
private:
int a;
};
class B:public A
{
public:
B() :b(1) { cout << "B cons" << endl; }
private:
int b;
};
void test()
{
B b; //B继承了A的成员a,a由A的构造函数初始化 所以这里会先调用A() 再调用B()
}
可以在派生类构造函数中调用基类构造,看下面代码
class A
{
public:
A(int x = 0) :a(x) { cout << "A cons" << endl; }
private:
int a;
};
class B:public A
{
public:
B(int x = 0, int y = 1) :A(x), b(y) { cout << "B cons 2" << endl; }
private:
int b;
};
4、存在继承关系的类型之间的转换规则
- 从派生类向基类的类型转换只对指针或引用类型有效
- 基类向派生类不存在隐式类型转换
- 派生类向基类的类型转换可能会由于访问受限而变得不可行,虽然可以将一个派生类对象拷贝、移动或赋值给一个基类对象,不过需要注意的是,这种操作只处理派生类对象中基类部分
5、override
派生类如果定义了一个函数与基类中虚函数的名字相同但是参数列表不同,如果没有override的话,编译器将认为新定义的函数与基类中原有的函数是独立的,合法。但如果加了override后,编译器在基类中找不到对应的虚函数就会报错了
6、虚函数与默认实参
如果虚函数使用默认实参,则基类和派生类中定义的默认实参最好一致,否则将以基类中的为准。即使实际运行的是派生类中的函数版本。
class B
{
public:
virtual void print(int x = 0)
{
cout << x << endl;
}
};
class D:public B
{
public:
void print(int x = 1)
{
cout << x << endl;
}
private:
};
void test()
{
B* b = new D();
b->print(); //输出0
D* d = new D();
d->print(); //输出1
}
7、回避虚函数的机制
有时候,我们希望对虚函数的调用不要进行动态绑定,而是强迫其执行虚函数的某个特定版本。使用作用域运算符可以实现这一目的。而通常只有成员函数或友元中的代码才需要使用作用域运算符来回避虚函数机制。
void test()
{
B* b = new D();
b->print(); //输出0
b->D::print(); //错误 限定名不是类B 或其基类
D* d = new D();
d->print(); //输出1
d->B::print();//输出0
}
8、纯虚函数
- 可以为纯虚函数提供定义,但是必须定义在类外
- 含有纯虚函数的类是抽象基类,不能创建抽象基类的对象
- 派生类必须覆盖抽象基类的纯虚函数
9、protected访问控制
- 和私有成员类似,受保护的成员对于类的用户来说不可访问
- 和公有成员类似,受保护的成员对于派生类的成员和友元来说是可访问的
- 派生类的成员或友元只能通过派生类对象(而不是基类对象)来访问基类的受保护成员,即派生类的成员和友元只能访问派生类对象中基类部分的受保护成员
class B
{
protected:
int b;
};
class D:public B
{
friend void printD(D&); //能访问D::d 和 D从B继承来的b
friend void printB(B&); //不饿能访