1. 组合与继承
例子
// 组合
class ClassA {
ClassB member;
};
// 继承
class BaseClass {
};
class DerivedClass : public ClassA {
};
注意点:
- 构造顺序:由内而外
- 析构顺序:由外而内
三种继承方式
- 公有继承(public)
公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。 - 私有继承(private)
私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。 - 保护继承(protected)
保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
2. 虚函数与多态
例子
class BaseClass {
public:
virtual void func() { puts("base"); }
void func2() { puts("base"); }
virtual ~BaseClass() {}
};
class DerivedClass : public ClassA {
public:
virtual void func() { puts("derived"); }
void func2() { puts("derived"); }
virtual ~DerivedClass() {}
};
void test() {
BaseClass* t = new DerivedClass;
// 非virtual函数,父类的被调用;virtual函数,子类被调用
t->func(); // derived
t->func2(); // base
delete t;
}
注意点:
- 析构函数要写成虚函数,这样子类才能被正确地释放内存;
- 父类定义成virtual的函数,子类即使不写virtual关键字,该函数也是virtual的;不过为了可读性好,子类最好也写上virtual;
- 对于父类virtual函数,子类如果有同名但不同型的函数,则构成overload,而不是override父类的virtual函数;
- C++11起子类复写父类的virtual函数时可以在函数尾加上override关键字,编译器会帮忙检查是否正确复写。
class BaseClass {
public:
virtual void func() { puts("base"); }
};
class DerivedClass : public ClassA {
public:
virtual void func() override { puts("derived"); }
};
虚函数实现原理
每当创建一个包含有虚函数的类或从包含有虚函数的类派生一个类时,编译器就为这个类创建一个虚函数表:VTABLE,这个表中存储了这个类及它的基类中所有声明为virtual函数的地址,如果这个派生类有复写父类的virtual函数,则放置的是派生类中的函数地址,如果没有,则放置的是父类中的函数地址。对于每一个该类对象,在对象体中都会含有一个指向该VTABLE的指针,用于查找VTABLE中的函数地址,该指针被成为VPTR,通常位于对象的头部。
class Base {
virtual void f();
virtual void g();
virtual void h();
};
class Derive {
virtual void f();
virtual void g1();
virtual void h1();
};
3. 委托设计
例子
class Delegate {
virtual func() = 0;
};
class TestClass {
Delegate* delegate = nullptr;
public:
void setDelegate(Delegate* d) { delegate = d; }
void test() { if (delegate) delegate->func() };
};
class ImplClassA : public Delegate {
virtual func() { puts("delegate class A"); }
};
class ImplClassB : public Delegate {
virtual func() { puts("delegate class B"); }
};
void test() {
TestClass a;
a.setDelegate(new ImplClassA);
a.test();
a.setDelegate(new ImplClassB);
a.test();
// ...
}