模板化函数或具有指向基类的指针的函数

Templated function or function with pointer to base class

本文关键字:函数 基类 指针      更新时间:2023-10-16

当我想为几种不同类型的输入类使用一个函数(这里称为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 代码时,我没有看到两者的直接优势(不过这可能与编译开关有关(。因此,我在这里没有考虑什么,在比较这两种方法时,这两种方法都有其优点/缺点吗?或者我是否可以将第三种方法用于相同的目的?

一般来说,第一个选项涉及虚拟调度(即在运行时跳转到正确的函数(,而第二个选项已经知道在编译时调用的正确函数。后者通常具有较少的开销,并为编译器开辟了更多的优化机会,但可能存在缺点(代码大小,指令缓存等(。性能将始终取决于细节,因此,如果您关心它,请配置文件。

开箱即用的继承无法做到的事情是,例如从work_func返回不同类型的值 - 这就是模板闪耀的地方。

另一方面,继承(特别是在遵循 Liskov 替换原则时(可以使接口的合约/期望更加清晰(void do_some_templated_work(T &test_class)不会告诉您T需要实现例如print_class_name(。