- 概述
- 类的派生会形成嵌套域
- 虚函数
- 继承与特殊成员函数
- 补充知识
通过类的继承(派生)来引入 是一个 的关系
被继承的类——基类,继承的类——继承类。
#includeusing namespace std; struct Base { }; struct Base2 : Base { }; // 这是类的声明,下面继承部分不是类的声明 struct Drive; // Drive派生自Base基类 // struct不写public,默认也是public继承 // 但是如果是class,那么默认是private继承,所以class经常看到public继承 struct Drive : public Base2 { }; int main() { Drive d; // 使用基类的指针或引用可以指向派生类对象 // Base2继承自Base,Drive继承自Base2,还是可以的 Base& ref = d; Base* ptr = &d; }
使用基类的指针或引用可以指向派生类对象
静态类型 V.S. 动态类型
静态类型:编译器就可以确定的类型。
动态类型:在运行期为ref或者指针赋予的实际的类型。
#includeusing namespace std; struct Base { void fun() {} }; struct Base2 : public Base { }; struct Drive : public Base2 { void fun2() {} }; struct Drive2 : public Base2 { }; int main() { Drive d; Base& ref = d; ref.fun(); // 编译器只看静态类型是否可以接收 // ref.fun2(); // fun2是Drive的函数,是ref的动态类型,ref不能调用动态类型的函数 Drive d2; Base* ptr = &d; ptr = &d2; // ptr对应的静态类型没有发生改变,还是Base。但是动态类型从Drive到Drive2了 }
protected 限定符:派生类可访问
#include类的派生会形成嵌套域using namespace std; struct Base { protected: // 如果是protected,那么Derive的fun2,就可以调用了 void fun() {} int x; }; struct Derive : public Base { public: void fun2() { fun(); // fun如果是private,无法访问 x; // 因为proteted,所以可以访问fun和x } }; int main() { Derive d; Base b; // b.fun(); // fun private不行 d.fun2(); // b.fun2(); // b不能访问Derive的函数 }
派生类中的名称定义会覆盖基类
#includeusing namespace std; struct Base { int val = 2; }; struct Derive : public Base { public: void fun2() { cout << val << endl; } // int val = 3; }; int main() { Derive d; Base b; d.fun2(); // 3 因为Derive里的val覆盖了Base的val // 根本原因是Derive是嵌套在Base里的 cout << d.val << endl; // 如果把Derive的val注释掉,那么会去找Base的val。因为他们是嵌套的关系 }
使用域操作符显式访问基类成员
#includeusing namespace std; struct Base { int val = 2; }; struct Derive : public Base { public: void fun2() { cout << val << endl; cout << Base::val << endl; // 域操作符 } int val = 3; }; int main() { Derive d; Base b; d.fun2(); // 3 因为Derive里的val覆盖了Base的val }
在派生类中调用基类的构造函数
#includeusing namespace std; struct Base { Base () { cout << "Base constructor is called" << endl; } }; struct Derive : public Base { public: Derive () { cout << "Derive constructor is called" << endl; } }; int main() { Derive d; // Base构造函数先调用,再调用Derive构造函数 }
#include虚函数using namespace std; struct Base { Base (int) { cout << "Base constructor is called" << endl; } }; struct Derive : public Base { public: Derive (int a) : Base(a) // 通过初始化列表来去调用基类Base的构造函数 { cout << "Derive constructor is called" << endl; } }; int main() { Derive d(100); // Base构造函数先调用,再调用Derive构造函数 }
通过虚函数与引用(指针)实现动态绑定
使用关键字 virtual 引入
非静态、非构造函数可声明为虚函数
虚函数会引入vtable结构
#includeusing namespace std; struct Base { virtual void baseMethod() {} // 虚函数 int baseMember; }; struct myClassDerive : public Base { virtual void deriveMethod() {} // 虚函数 int deriveMember; }; struct myClassDerive2 : public myClassDerive { virtual void deriveMethod2() {} // 虚函数 int deriveMember2; }; int main() { myClassDerive2 d; Base& b = d; Base* ptr = &d; myClassDerive2& d2 = dynamic_cast (b); myClassDerive2* ptr2 = dynamic_cast (ptr); }
如果我们想把一个基类的指针转成一个派生类的指针,如果基类的指针确实指向派生类的地址,那么这个转换就是成功的,否则指向空指针。基类的引用转换成派生类的引用,如果基类引用确实绑定在派生类的引用,就是成功的,否则抛出异常。
static_cast是在编译期处理的,但是dynamic_cast是在运行期发生的,判断动态类型能不能发生转变。如果要编写高性能的程序,dynamic_cast慎用。
#includeusing namespace std; struct Base { virtual void fun() { cout << "Base :fun() is called" << endl; } }; struct Derive : Base { void fun() { cout << "Derive :fun() is called" << endl; } }; int main() { Derive d; d.fun(); Base& b = d; b.fun(); // 注意,使用虚函数,那么就会调用Derive的fun,但是如果没有使用虚函数,就会使用Base里的fun // 没有了虚函数,就没有个vtable。所以对象的绑定都会在编译期确定,而编译期只看静态类型,b就是Base,所以只能调用Base中的fun }
虚函数在派生类中的重写( override )
#includeusing namespace std; struct Base { virtual void fun() { cout << "Base :fun() is called" << endl; } }; struct Derive : Base { // override 重写 void fun() { cout << "Derive :fun() is called" << endl; } }; void proc(Base& b) { b.fun(); } int main() { // 动态类型实现的运行期的一个多态,也叫运行期多态 Derive d; proc(d); // 这个参数的动态类型是derive,所以调用Derive的fun Base b; proc(b); // 这个参数动态、静态类型都是Base,所以调用Base的fun }
函数签名保持不变(返回类型可以是原始返回指针 / 引用类型的派生指针 / 引用类型)
#includeusing namespace std; struct Base2 {}; struct Derive2 : Base2 {}; struct Base { virtual Base2& fun() { cout << "Base :fun() is called" << endl; static Base2 b; return b; } }; struct Derive : Base { // override 重写 Derive2& fun() { cout << "Derive :fun() is called" << endl; static Derive2 inst; return inst; } }; void proc(Base& b) { b.fun(); } int main() { // 动态类型实现的运行期的一个多态,也叫运行期多态 Derive d; proc(d); // 这个参数的动态类型是derive,所以调用Derive的fun Base b; proc(b); // 这个参数动态、静态类型都是Base,所以调用Base的fun }
可以通过 = 0 声明纯虚函数,相应地构造抽象基类
虚函数特性保持不变
override 关键字



