【发布时间】:2020-01-10 16:37:12
【问题描述】:
当我想为几种不同类型的输入类使用一个函数(这里称为do_some_work()/do_some_templated_work())并且我不想多次重写该函数时,据我所知,我可以使用模板函数,或从基类派生所有涉及的类,并使用该基类的指针。我为这两种情况编写了一个简短的测试程序:
#include <iostream>
#include <string>
class BaseClass {
public:
BaseClass(){
class_name = std::string("Base class");
}
virtual ~BaseClass(){};
virtual double work_func(const double a, const double b) {
(void) a;
(void) b;
return 0;
};
virtual void print_class_name(void) {};
private:
std::string class_name;
};
class DerivedClassA : public BaseClass{
public:
DerivedClassA(){
class_name = std::string("Class A");
}
~DerivedClassA() override {
}
double work_func(const double a, const double b) override{
return a + b;
}
void print_class_name(void) override{
std::cout << class_name << '\n';
}
private:
std::string class_name;
};
class DerivedClassB : public BaseClass{
public:
DerivedClassB(){
class_name = std::string("Class B");
}
~DerivedClassB() override {
}
double work_func(const double a, const double b) override{
return a - b;
}
void print_class_name(void) override{
std::cout << class_name << '\n';
}
private:
std::string class_name;
};
void do_some_work(BaseClass &test_class){
test_class.print_class_name();
std::cout << test_class.work_func(5, 6) << '\n';
}
template <class T>
void do_some_templated_work(T &test_class) {
test_class.print_class_name();
std::cout << test_class.work_func(5, 6) << '\n';
}
int main()
{
std::cout << "Hello World!" << std::endl;
DerivedClassA AClass;
DerivedClassB BClass;
do_some_work(AClass);
do_some_work(BClass);
do_some_templated_work(AClass);
do_some_templated_work(BClass);
return 0;
}
在查看 ASM 代码时,我没有看到其中任何一个的直接优势(不过,这可能与编译开关有关)。因此,这里有什么我没有考虑的事情吗?在比较两者时,这两种方法是否各有优缺点?或者是否有第三种方法可以用于相同目的?
【问题讨论】:
-
你知道纯虚函数吗?如果你写
= 0;而不是BaseClass中那些虚函数的无意义的函数体,它就变成了一个抽象基类。如果您不熟悉它,我建议您研究一下。 -
此外,如果您只是通过重新声明将
class_name隐藏在所有派生类中,那么在基类中声明class_name是没有意义的。成员变量不能被覆盖或类似的东西。 -
“在查看 ASM 代码时,我没有看到其中任何一个的直接优势” 对于那个玩具示例,编译器可能会进行非常好的优化(去虚拟化),使上述选择相同.如果它不做这些优化,你的静态分派(模板函数)仍然对运行时分派有作用,因为来自
T的调用方法不是final(T不一定是最派生的类型)。