C++中的override关键字有什么作用?
override关键字在派生类中使用,告诉编译器,它所修饰的函数必需重写(override)基类的某个虚函数,否则编译器要报错。 这样强制有什么用呢?一个很大的作用(唯一的作用?)就是能在编译阶段防止拼写错误导致的重写失败问题,举个例子,如下代码所示,故意拼错派生类的函数名。
#include <iostream>
class Base{
public:
virtual void print(){ cout<<"Base::print()"<<endl;}
};
class Derived : public Base{
public:
#if 0
void pirnt(){ cout<<"Derived::print()"<<endl;}
#else
void pirnt() override { cout<<"Derived::print()"<<endl;}
#endif
};
int main(){
Base *p = new Derived();
p->print();
}
情况一运行结果:编译器不报错,但是运行出现了错误,没出现我们希望的Derived::print()
情况二编译结果:编译器提示没有重写任何函数。
15:10: error: 'void Derived::pirnt()' marked override, but does not override
【小贴士】 别看重写失败是一个小问题,这有可能让开发者调试半天,善用override会让你减少潜在的调试时间。
多态中为何需要将析构函数设置为virtual类型?
如果不设置析构函数为虚函数,那么当使用基类指针指向派生类对象的方式(多态)来销毁对象时,派生类的析构函数就不会被执行。 如果派生类的析构函数承担了释放资源的任务,那么这种情况下资源将得不到释放,进而导致内存泄漏。
相关示例代码如下:
#include <iostream>
class Base{
public:
Base(){ cout<<"Base()"<<endl; }
#if 1
virtual ~Base(){ cout<<"~Base()"<<endl; }
#else
~Base(){ cout<<"~Base()"<<endl; }
#endif
};
class Derived : public Base{
public:
Derived(){ cout<<"Derived()"<<endl; }
~Derived() override { cout<<"~Derived()"<<endl; }
};
int main(){
Base *p = new Derived();
}
情况一运行结果(正确):
Base()
Derived()
~Derived()
~Base()
情况二运行结果(错误):
【小贴士】 基类的析构函数名为~Base(),派生类的析构函数名为~Derived(),虽然它们的名字不同,但是它们确实可以是重写(override)的关系,代码中的“override”也证实了这一点。
【小贴士】 多态模式下的析构顺序:首先,根据虚函数表指针,找到了派生类的析构函数,并执行之,析构函数返回前最后一件事(比函数内最后一行代码还晚)就是执行基类的析构函数,于是得到了我们想要的结果。
如何优雅地定义没有成员函数的类型为虚类?
class Base
{
public:
virtual ~Base() = 0;
...
};
Base::~Base() {}
注意,析构函数不要定义在头文件中,否则链接的时候会报redefinition错误。
如何优雅的拷贝虚类对象?
往往我们拿到的对象指针是虚类,要拷贝虚类对象,直接调用虚类的拷贝构造函数是不行的,优雅的方法是在虚类中定义一个clone()函数,让派生类去实现这个函数。
class Base {
public:
virtual Base* Clone() = 0;
};
class Derivedn : public Base {
public:
Derivedn* Clone() {
return new Derivedn(*this);
}
private:
Derivedn(const Derivedn) : ... {}
};
抽象类的构造函数不能运行成员虚成员函数
其实原因很简单,当派生类在构造过程中,自己到底是什么类型是不知道的,从而不知道要调用那种类型的成员函数。 只有构造完毕才能知道,实际上虚函数表指针也是在构造函数中进行赋值的。
struct Abstract {
virtual void pure() = 0;
virtual void foo() {
pure();
}
Abstract() {
foo();
}
~Abstract() {
foo();
}
};
struct X : public Abstract {
virtual void pure() { cout << " X :: pure() " << endl; }
virtual void impure() { cout << " X :: impure() " << endl; }
};
int main() {
X x;
}
\endcpp
\section 基类和派生类可以定义相同名称的成员
如果不是明确的需要基类和派生类各自需要同名称的变量,普通情况下,千万不要在派生类里重复定义变量,因为两者的含义并不相同,会导致结果不是我们想要的。
\code{.cpp}
#include <iostream>
class A{
public:
int x = 5;
};
class B : public A{
public:
int x = 10;
};
int main()
{
B b;
cout<<"B.x="<<b.x<<endl;
A *a = &b;
cout<<"A->x="<<a->x<<endl;
}
重写(override)父类的重载(overload)函数
#include <iostream>
#include <string>
class Base{
public:
virtual void func(int a) = 0;
virtual void func(float a) = 0;
virtual void func(int a, int b) = 0;
};
class Derived : Base{
public:
void func(int a) { cout<<"int func 1"<<endl; }
void func(float a) { cout<<"float func 1"<<endl; }
void func(int a, int b) {cout<<"int func 1,2"<<endl;}
};
int main()
{
Derived d;
d.func(1);
d.func(1.0);
d.func(1,2);
}
重写函数可以子类型
重写函数不一定要返回类型和参数类型完全一致,这些类型的派生类也是可以接受并认为是派生的。
#include <iostream>
#include <string>
class Base{
public:
virtual Base * clone() = 0;
};
class Derived : Base{
public:
Derived(){cout<<"Derived()"<<endl;}
Derived * clone() override { return new Derived();}
};
int main()
{
Derived d;
d.clone();
}
这样的好处是:派生类重写成员函数的时候,不在需要内部一个强行的指针转换,在重写的成员函数数量比较多的时候,还是挺能节省时间的。
遗憾的是,如果这些指针被shared_ptr封装,就不能这样写了。
#include <iostream>
#include <string>
class Base{
public:
virtual shared_ptr<Base> * clone() = 0;
};
class Derived : Base{
public:
Derived(){cout<<"Derived()"<<endl;}
shared_ptr<Derived> * clone() override { return shared_ptr<Dervied>(new Derived());}
};
int main()
{
Derived d;
d.clone();
}
基于基类指针获取子类的size?
没有直接的方法,但是可以在基类定义一个虚函数size(),子类实现就好了。
struct base {
virtual size_t size() const =0;
virtual ~base() { }
};
template<typename T>
struct intermediate : base {
virtual size_t size() const { return sizeof(T); }
};
struct derived : intermediate<derived>
{ };