一、隐藏(重定义)
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的析构函数。最后一行打印的是派生类析构完,基类析构。
class A
{
public:
A()
{
cout << "A\n";
}
virtual ~A()
{
cout << "~A\n";
}
};
class B : public A
{
public:
B()
{
cout << "B\n";
}
virtual ~B()
{
cout << "~B\n";
}
};
总结:
A* a = new a; B* b = new b; delete a; detele b; | A* a = new a; B* b = new b; delete a; detele b;(两个析构为虚析构函数) | A* a = new B; delete a; | A* a = new B; delete a;(两个析构为虚析构函数) | A* a = new A;B* b = dynamic_cast<B*>(a);delete a;delete b; |
---|---|---|---|---|
A A B ~A ~B ~A | A A B ~A ~B ~A | A B ~A | A B ~B ~A | A ~A |
四、区别
隐藏(重定义) | 重载 | 重写(覆盖) | |
---|---|---|---|
范围 | 只在继承关系的两个类中 | 一个类中的两个函数 | 只在继承关系的两个类中 |
形成条件 | 同名 | 同名但能参数列表不同 | 虚函数,完全相同(例外:协变) |