C++ 为了代码重用而不必要地使用虚拟函数
我使用继承只是为了代码重用,我从不将类强制转换为基类。这是一个高性能的程序,所以我想避免使用C++ 为了代码重用而不必要地使用虚拟函数,c++,oop,inheritance,C++,Oop,Inheritance,我使用继承只是为了代码重用,我从不将类强制转换为基类。这是一个高性能的程序,所以我想避免使用虚拟函数,但我不知道如何使用。考虑下面的代码 class A{ public: void print(){ std::cout << "Result of f() is: " << f() << std::endl; } virtual std::string f(){ return "A"; } }; class B : publi
虚拟
函数,但我不知道如何使用。考虑下面的代码
class A{
public:
void print(){
std::cout << "Result of f() is: " << f() << std::endl;
}
virtual std::string f(){
return "A";
}
};
class B : public A{
public:
virtual std::string f(){
return "B";
}
};
A类{
公众:
作废打印(){
std::cout如果出于任何原因,您不希望动态绑定的“开销”,您可以省略virtual
-关键字,这使得编译器使用静态绑定并禁用多态性。也就是说,编译器将在编译时仅根据变量的类型绑定实现,而不是在运行时
老实说,我从来没有在没有启用多态性的情况下为子类中的方法定义过特定的实现。这样做通常表明没有特定的行为,即该方法根本不应该在子类中被重写
此外,将字符常量封装到字符串对象中的代码的性能成本已经远远高于动态绑定/vtable
-“开销”。实际上,在进行此类“优化”之前,请重新考虑它两次,然后衡量性能的提高
无论如何,如果省略virtual
,请查看代码的行为。请注意,代码中的ptr->f()
绑定到A::f
,因为变量的类型是A*
,尽管它指向类型为B
的对象:
class A{
public:
void print(){
std::cout << "Result of f() is: " << f() << std::endl;
}
std::string f(){
return "A";
}
};
class B : public A{
public:
std::string f(){
return "B";
}
};
int main()
{
A a; cout << a.f(); // -> yields "A"
B b; cout << b.f(); // -> yields "B"
A* ptr = &b; cout << ptr->f(); // -> yields "A"; (virtual f, in contrast) would be "B"
return 0;
}
A类{
公众:
作废打印(){
std::cout如果出于任何原因,您不希望动态绑定的“开销”,您可以省略virtual
-关键字,这使得编译器使用静态绑定并禁用多态性。也就是说,编译器将在编译时仅根据变量的类型绑定实现,而不是在运行时
老实说,我从来没有在没有启用多态性的情况下为子类中的方法定义过特定的实现。这样做通常表明没有特定的行为,即该方法根本不应该在子类中被重写
此外,将字符常量封装到字符串对象中的代码的性能成本已经远远高于动态绑定/vtable
-“开销”。实际上,在进行此类“优化”之前,请重新考虑它两次,然后衡量性能的提高
无论如何,如果省略virtual
,请查看代码的行为。请注意,代码中的ptr->f()
绑定到A::f
,因为变量的类型是A*
,尽管它指向类型为B
的对象:
class A{
public:
void print(){
std::cout << "Result of f() is: " << f() << std::endl;
}
std::string f(){
return "A";
}
};
class B : public A{
public:
std::string f(){
return "B";
}
};
int main()
{
A a; cout << a.f(); // -> yields "A"
B b; cout << b.f(); // -> yields "B"
A* ptr = &b; cout << ptr->f(); // -> yields "A"; (virtual f, in contrast) would be "B"
return 0;
}
A类{
公众:
作废打印(){
std::cout当可以静态地确定为特定方法调用什么实现方法时,通常用于避免动态调度
在您的示例中,A
和B
都将从一个基类继承,该基类提供了print()
方法。基类,我们称之为print
,是一个模板,其模板参数是一个提供f()
的类。这种扭曲使这种模式变得“奇怪”名字是子类必须继承在子类上模板化的基类。这允许子类访问基类的print
方法,但获得基类的一个版本,并扩展为调用自己的f
的print
下面是一个工作代码示例:
#include <iostream>
template<typename F>
class Print {
public:
void print() {
F& final = static_cast<F&>(*this);
std::cout << "Result of f() is: " << final.f() << std::endl;
}
};
class A: public Print<A> {
public:
std::string f(){
return "A";
}
};
class B: public Print<B> {
public:
std::string f(){
return "B";
}
};
int main() {
A a;
B b;
a.print();
b.print();
}
#包括
模板
类打印{
公众:
作废打印(){
F&final=静态铸件(*本);
std::cout当可以静态地确定为特定方法调用什么实现方法时,通常用于避免动态调度
在您的示例中,A
和B
都将从一个基类继承,该基类提供了print()
方法。基类,我们称之为print
,是一个模板,其模板参数是一个提供f()
的类。这种扭曲使这种模式变得“奇怪”名字是子类必须继承在子类上模板化的基类。这允许子类访问基类的print
方法,但获得基类的一个版本,并扩展为调用自己的f
的print
下面是一个工作代码示例:
#include <iostream>
template<typename F>
class Print {
public:
void print() {
F& final = static_cast<F&>(*this);
std::cout << "Result of f() is: " << final.f() << std::endl;
}
};
class A: public Print<A> {
public:
std::string f(){
return "A";
}
};
class B: public Print<B> {
public:
std::string f(){
return "B";
}
};
int main() {
A a;
B b;
a.print();
b.print();
}
#包括
模板
类打印{
公众:
作废打印(){
F&final=静态铸件(*本);
std::cout您可以使用模板来选择A和B的动态或非动态版本。这是一个相当棘手/难看的选项,但值得考虑
#include <string>
template <bool Virt = false>
class A{
public:
std::string f(){
return "A";
}
};
template <>
class A<true> : A<false>{
public:
virtual std::string f(){
return A<false>::f();
}
};
template <bool Virt = false>
class B : public A<Virt>{
public:
std::string f(){
return "B";
}
};
std::string f1() { return B<>().f(); }
std::string f2(A<true> &a) { return a.f(); }
std::string f3() { B<true> b; return f2(b); }
#include <iostream>
int main(){
std::cout << f1() << '\n';
std::cout << f3() << '\n';
return(0);
}
#包括
模板
甲级{
公众:
std::string f(){
返回“A”;
}
};
模板
A类:A{
公众:
虚拟标准::字符串f(){
返回A::f();
}
};
模板
B类:公共A{
公众:
std::string f(){
返回“B”;
}
};
std::string f1(){返回B().f();}
std::string f2(A&A){返回A.f();}
std::string f3(){B B;返回f2(B);}
#包括
int main(){
std::cout您可以使用模板来选择A和B的动态或非动态版本。这是一个相当棘手/难看的选项,但值得考虑
#include <string>
template <bool Virt = false>
class A{
public:
std::string f(){
return "A";
}
};
template <>
class A<true> : A<false>{
public:
virtual std::string f(){
return A<false>::f();
}
};
template <bool Virt = false>
class B : public A<Virt>{
public:
std::string f(){
return "B";
}
};
std::string f1() { return B<>().f(); }
std::string f2(A<true> &a) { return a.f(); }
std::string f3() { B<true> b; return f2(b); }
#include <iostream>
int main(){
std::cout << f1() << '\n';
std::cout << f3() << '\n';
return(0);
}
#包括
模板
甲级{
公众:
std::string f(){
返回“A”;
}
};
模板
A类:A{
公众:
虚拟标准::字符串f(){
返回A::f();
}
};
模板
B类:公共A{
公众:
std::string f(){
返回“B”;
}
};
std::string f1(){返回B().f();}
std::string f2(A&A){返回A.f();}
std::string f3(){B B;返回f2(B);}
#包括
int main(){
std::cout继承不是获得代码重用的好方法。如果您想避免虚拟多态性,请查找CRTP。但在大多数情况下,vtables的性能影响被高估。重复前面的注释,请注意过早优化。您应该通过经验验证