1、子类和父类中有同名成员或函数(非虚函数),子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。 2、如果是成员函数的隐藏,只需要函数名相同就构成隐藏。 3、在继承体系中基类和派生类都有独立的作用域。
class Base //基类 { public: int m_a;//重名成员变量 Base() : m_a(10) {} void func() //重名成员函数 { cout << "Base" << endl; } }; class Test :public Base //派生类 { public: int m_a;//重名成员变量 Test() : m_a(20) {} void func()//重名成员函数 { cout << "Test" << endl; } };此时,基类的成员将被隐藏,要想访问,两种方法: 1.定义一个基类的指针赋值给派生类,再通过基类的指针进行访问。 2.使用基类的作用域调用。
//方法1: Test T ; Base *Bptr = &T; cout << T.m_a<< endl; T.func(); cout << Bptr->m_a << endl; Bptr->func(); //方法2: Test T; cout << T.m_a<< endl; T.func(); cout << T.Base::m_a << endl; T.Base::func();建议:
1、不要把基类的地址赋值给派生类指针,极有可能产生越界 2、不要在派生类中定义跟基类成员同名的成员,也就不会右隐藏的问题了。
只要函数名相同,参数列表不同就构成函数重载关系(只是返回值类型相同也不行)。
int add(int a, int b) { return a + b; } int add(int a, int b,int c) { return a + b + c; } int main() { cout << add(1, 2) << endl; cout << add(1, 2, 3) << endl; return 0; }1.定义:
1.基类里有一个虚函数和派生类里的一个虚函数完全一致(返回值,函数名,参数列表),则基类里的这个函数被派生类里的那个重名函数所重写(覆盖)。 2.注意:重写只是在派生类中调用派生类里的那个虚函数,不会继承基类里的那个虚函数,而不会去改变基类里的那个重名的虚函数。 3.如果基类的函数为虚函数,此时派生类的函数只要定义,无论是否加virtual关键字,都与基类的函数构成重写。
class Base//基类 { public: int m_a; virtual void func()//基类虚函数 { cout << "Base" << endl; } }; class Test : public Base//派生类 { public: int m_b; virtual void func()//派生类虚函数 { cout << "Test" << endl; } }; int main() { Base B; Test T; Base* Bptr = &T; T.func(); Bptr->func(); B.func(); system("pause"); return 0; }当基类里的虚函数和派生类的函数构成重载关系是
2.特例:协变
协变(基类与派生类虚函数返回值类型不同) 派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引 用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{}; class B : public A {}; class Person { public: virtual A* f() { return new A; } }; class Student : public Person { public: virtual B* f() { return new B; } };协变的深入了解
3.析构函数的重写
因为在C++中,类中的析构函数底层都是由destructor函数实现的,所以,经过编译后,基类和派生类析构函数都会处理成destructor,从而基类的析构函数会被派生类的析构函数重。
class Base { public: virtual~Base() { cout << "~Base" << endl; } }; class Test : public Base { public: virtual~Test() { cout << "~Test" << endl; } }; int main() { Base* B1 = new Base; Base* B2 = new Test;//用一个基类对象指针指向一个派生类 delete B1;//new一个基类对象,delete掉,没有涉及继承,所以只调用自己的析构函数。 delete B2; system("pause"); return 0; }
在这里,B2是一个指向Test对象的Base*指针,目的是限制B2的作用域,B2只能作用于继承的基类那部分,基类的析构函数输出为 ~~Base,如果不被重写,则第二行应该打印的是~Base,由于析构函数底层实现的原理一致构成了重写,调用了Test的析构函数。最后一行打印的是派生类析构完,基类析构。