调用的子类函数要满足: A derived function is considered a match if it has the same signature (name, parameter types, and whether it is const) and return type as the base version of the function. Such functions are called overrides.
1#include<iostream> 2usingnamespace std; 3classA 4 { 5public: 6virtualconstchar* getName(){ return"A"; } 7 }; 8 9classB: public A 10 { 11public: 12virtualconstchar* getName(){ return"B"; } 13 }; 14 15classC: public B 16 { 17public: 18virtualconstchar* getName(){ return"C"; } //or const char * getName(){..} and default virtual 19 }; 20 21classD: public C 22 { 23public: 24// virtual const char* getName() { return "D"; } 25 }; 26 27intmain() 28 { 29 D d; 30 A &rBase = d; 31 std::cout << "rBase is a " << rBase.getName() << '\n'; 32 33return0; 34 }``` 输出c #### 注意点: + virtual关键字是否都需要写?: Only the most base classfunction needs to be tagged as virtualfor all of the derived functions to work virtually. However, having the keyword virtual on the derived functions does not hurt, and it serves as a useful reminder that the function is a virtual function rather than a normal one. + 不能在构造函数和西沟函数中调用它,因为此时子类对象还没有被构造 + c++11引入override和final来防止避免不匹配的复写和阻止继承: 1)override: 出现错误的例子: ```cpp classA { public: virtualconstchar* getName1(int x){ return"A"; } virtualconstchar* getName2(int x){ return"A"; } }; classB : public A { public: virtualconstchar* getName1(shortint x){ return"B"; } // note: parameter is a short int virtualconstchar* getName2(int x)const{ return"B"; } // note: function is const }; intmain() { B b; A &rBase = b; std::cout << rBase.getName1(1) << '\n'; std::cout << rBase.getName2(2) << '\n'; return0; }
加入override: classA { public: virtualconstchar* getName1(int x){ return"A"; } virtualconstchar* getName2(int x){ return"A"; } virtualconstchar* getName3(int x){ return"A"; } }; classB : public A { public: virtualconstchar* getName1(shortint x)override{ return"B"; } // compile error, function is not an override virtualconstchar* getName2(int x)constoverride{ return"B"; } // compile error, function is not an override virtualconstchar* getName3(int x)override{ return"B"; } // okay, function is an override of A::getName3(int) }; intmain() { return0; } Rule: Apply the override specifier to every intended override function you write.
加了final的函数无法被复写: classA { public: virtualconstchar* getName(){ return"A"; } }; classB : public A { public: // note use of final specifier on following line -- that makes this function no longer overridable virtualconstchar* getName()overridefinal{ return"B"; } // okay, overrides A::getName() }; classC : public B { public: virtualconstchar* getName()override{ return"C"; } // compile error: overrides B::getName(), which is final }; 加了final的类不能被继承: classA { public: virtualconstchar* getName(){ return"A"; } }; classBfinal : public A // note use of final specifier here { public: virtualconstchar* getName()override{ return"B"; } }; classC : public B // compile error: cannot inherit from final class { public: virtualconstchar* getName()override{ return"C"; } }; ```` + 对匹配返回值的一个“例外”:covariant return types: ```cpp class Base { public: // This version of getThis() returns a pointer to a Base class virtual Base* getThis() { returnthis; } }; classDerived: public Base { // Normally override functions have to return objects of the same type as the base function // However, because Derived is derived from Base, it's okay to return Derived* instead of Base* virtual Derived* getThis(){ returnthis; } };```
注意当base=&derive; base.getThis()----取得的任然是base #### 当想基类指针指向派生类,虚函数但仍然想用基类虚函数时: ```cpp #include<iostream> intmain() { Derived derived; Base &base = derived; // Calls Base::GetName() instead of the virtualized Derived::GetName() std::cout << base.Base::getName() << std::endl; }
#include<iostream> classBase { public: ~Base() // note: not virtual { std::cout << "Calling ~Base()" << std::endl; } }; classDerived: public Base { private: int* m_array; public: Derived(int length) { m_array = newint[length]; } ~Derived() // note: not virtual { std::cout << "Calling ~Derived()" << std::endl; delete[] m_array; } }; intmain() { Derived *derived = newDerived(5); Base *base = derived ; delete base; return0; }``` 只输出Calling ~Base() + 所以为了调用派生类的析构函数,需要定义为虚析构函数: ```cpp #include<iostream> classBase { public: virtual ~Base() // note: virtual { std::cout << "Calling ~Base()" << std::endl; } }; classDerived: public Base { private: int* m_array; public: Derived(int length) { m_array = newint[length]; } virtual ~Derived() // note: virtual { std::cout << "Calling ~Derived()" << std::endl; delete[] m_array; } }; intmain() { Derived *derived = newDerived(5); Base *base = derived; delete base; return0; } Now this program produces the following result: Calling ~Derived() Calling ~Base() Rule: Whenever you are dealing with inheritance, you should make any explicit destructors virtual. ``` ### 虚表: #### Early binding(静态绑定) and late binding(动态绑定) 前者指的是直接在代码里调用函数,通过函数名,即编译期间就可以直到函数的地址的(偏移,非真实的内存地址) ""Early binding(also called static binding) means the compiler(or linker) is able to directly associate the identifier name(such as a function or variable name) with a machine address. Remember that all functions have a unique address. So when the compiler(or linker) encounters a function call, it replaces the function call with a machine language instruction that tells the CPU to jump to the address of the function."" ```cpp #include<iostream> intadd(int x, int y) { return x + y; } intsubtract(int x, int y) { return x - y; } intmultiply(int x, int y) { return x * y; } intmain() { int x; std::cout << "Enter a number: "; std::cin >> x; int y; std::cout << "Enter another number: "; std::cin >> y; int op; do { std::cout << "Enter an operation (0=add, 1=subtract, 2=multiply): "; std::cin >> op; } while (op < 0 || op > 2); int result = 0; switch (op) { // call the target function directly using early binding case0: result = add(x, y); break; case1: result = subtract(x, y); break; case2: result = multiply(x, y); break; } std::cout << "The answer is: " << result << std::endl; return0; }``` 后者指的是通过函数指针等方式,即在编译期间无法确定调用什么函数,需要通过指针来访问函数(函数指针),效率比前者低 in some programs, it is not possible to know which function will be called until runtime(when the program is run). This is known as late binding(or dynamic binding). In C++, one way to get late binding is to use function pointers. ```cpp #include<iostream> intadd(int x, int y) { return x + y; } intmain() { // Create a function pointer and make it point to the Add function int (*pFcn)(int, int) = add; std::cout << pFcn(5, 3) << std::endl; // add 5 + 3 return0; }
intmain() { Base base; // We can't instantiate an abstract base class, but for the sake of example, pretend this was allowed base.getValue(); // what would this do? }``` #### 为什么需要纯虚函数和纯虚类? 当子类继承了基类后,对虚函数来讲,需要复写基类的虚函数,而当忘记去写时,默认就会调用基类的虚函数; 而如果将基类的虚函数定义为纯虚函数。就会通过编译报错来提醒我们要复写子类的虚函数: 例子: ```cpp #include<string> classAnimal// This Animal is an abstract base class { protected: std::string m_name; public: Animal(std::string name) : m_name(name) { } std::string getName(){ return m_name; } virtualconstchar* speak()= 0; // note that speak is now a pure virtual function }; #include<iostream> classCow: public Animal { public: Cow(std::string name) : Animal(name) { } // We forgot to redefine speak }; intmain() { Cow cow("Betsy"); std::cout << cow.getName() << " says " << cow.speak() << '\n'; }
#include<string> #include<iostream> classAnimal// This Animal is an abstract base class { protected: std::string m_name; public: Animal(std::string name) : m_name(name) { } std::string getName(){ return m_name; } virtualconstchar* speak()= 0; // note that speak is a pure virtual function }; constchar* Animal::speak() { return"buzz"; // some default implementation } classDragonfly: public Animal { public: Dragonfly(std::string name) : Animal(name) { } virtualconstchar* speak()// this class is no longer abstract because we defined this function { return Animal::speak(); // use Animal's default implementation } }; intmain() { Dragonfly dfly("Sally"); std::cout << dfly.getName() << " says " << dfly.speak() << '\n'; } The above code prints: Sally says buzz
classPoweredDevice { }; classScanner: virtualpublic PoweredDevice { }; classPrinter: virtualpublic PoweredDevice { }; classCopier: public Scanner, public Printer { }; #include<iostream> classPoweredDevice { public: PoweredDevice(int power) { std::cout << "PoweredDevice: " << power << '\n'; } }; classScanner: virtualpublic PoweredDevice // note: PoweredDevice is now a virtual base class { public: Scanner(int scanner, int power) : PoweredDevice(power) // this line is required to create Scanner objects, but ignored in this case { std::cout << "Scanner: " << scanner << '\n'; } }; classPrinter: virtualpublic PoweredDevice // note: PoweredDevice is now a virtual base class { public: Printer(int printer, int power) : PoweredDevice(power) // this line is required to create Printer objects, but ignored in this case { std::cout << "Printer: " << printer << '\n'; } }; classCopier: public Scanner, public Printer { public: Copier(int scanner, int printer, int power) : Scanner(scanner, power), Printer(printer, power), PoweredDevice(power) // PoweredDevice is constructed here { } }; This time, our previous example: intmain() { Copier copier(1, 2, 3); } produces the result: PoweredDevice: 3 Scanner: 1 Printer: 2
这样的话,基类的构造交给了继承琏最底层的类
注意:
virtual base class在子类对象之前就创建了
if Copier was singly inherited from Printer, and Printer was virtually inherited from PoweredDevice, Copier is still responsible for creating PoweredDevice
Fourth, a virtual base class is always considered a direct base of its most derived class (which is why the most derived class is responsible for its construction).
But classes inheriting the virtual base still need access to it. So in order to facilitate this, the compiler creates a virtual table for each class directly inheriting the virtual class (Printer and Scanner). These virtual tables point to the functions in the most derived class. Because the derived classes have a virtual table, that also means they are now larger by a pointer (to the virtual table).
对象分割:
当子类对象赋值给基类会发生什么? 子类对象的基类部分会给基类对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
intmain() { Derived derived(5); Base base = derived; // what happens here? std::cout << "base is a " << base.getName() << " and has value " << base.getValue() << '\n'; return0; } 传值给基类 voidprintName(const Base base)// note: base passed by value, not reference { std::cout << "I am a " << base.getName() << '\n'; } This is a pretty simple function with a const base object parameter that is passed by value. If we call this function like such: intmain() { Derived d(5); printName(d); // oops, didn't realize this was pass by value on the calling end return0; }
vector和vector<&base>和vector<base*> 第一种可以但是只能调用基类的部分 第二种不行:std::vector<Base&> v; Unfortunately, this won’t compile. The elements of std::vector must be assignable, whereas references can’t be reassigned (only initialized). 第三种可以但是要做delete
#include <vector>
int main()
{
std::vector<Base*> v;
v.push_back(new Base(5)); // add a Base object to our vector
v.push_back(new Derived(6)); // add a Derived object to our vector
// Print out all of the elements in our vector
for (int count = 0; count < v.size(); ++count)
std::cout << "I am a " << v[count]->getName() << " with value " << v[count]->getValue() << "\n";
for (int count = 0; count < v.size(); ++count)
delete v[count];
return 0;
}
用智能指针可以避免:
#include <vector>
#include <functional> // for std::reference_wrapper
int main()
{
std::vector<std::reference_wrapper<Base> > v; // our vector is a vector of std::reference_wrapper wrapped Base (not Base&)
Base b(5); // b and d can't be anonymous objects
Derived d(6);
v.push_back(b); // add a Base object to our vector
v.push_back(d); // add a Derived object to our vector
// Print out all of the elements in our vector
for (int count = 0; count < v.size(); ++count)
std::cout << "I am a " << v[count].get().getName() << " with value " << v[count].get().getValue() << "\n"; // we use .get() to get our element from the wrapper
return 0;
}```
一种极端情况:
```cpp
int main()
{
Derived d1(5);
Derived d2(6);
Base &b = d2;//b为d2的引用
b = d1; // this line is problematic 导致b的基类部分为d1,子类部分为d2,因为b是d2的引用,导致现在d2的基类部分是d1而子类部分是d2
return 0;
}```
#### 总结多态的方式+dynamic_cast:
+ shape *ps=new circle();
经由virtual func: ps->rotate() //virtual func
经由dynamic_cast:和type运算符:
if(circle *pc=dynamic_cast<circle*>(ps))
(理解了指针类型的本质就可以理解它和理解分割的内容了:指针的类型会教导编译器如何解释某个特定地址的内容和大小:
所以一个void*的指针无法去确定涵盖多大的地址空间,所以它只能含有一个地址但是不能操作它指向的Object
所以转型只是一种编译器指令,不改变真正的地址,而是改变解读指针的方式让它指出其内存的大小和内容)
更多见内存布局第一章图就能理解)