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

Posted

技术标签:

【中文标题】模板化函数或带有指向基类的指针的函数【英文标题】:Templated function or function with pointer to base class 【发布时间】: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 的调用方法不是finalT 不一定是最派生的类型)。 【参考方案1】:

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

开箱即用的继承不能做的事情是,例如从 work_func 返回不同类型的值 - 这就是模板的亮点。

另一方面,继承(尤其是在遵循 Liskov 替换原则时)可以使接口的契约/期望更加清晰(void do_some_templated_work(T &amp;test_class) 并没有告诉您 T 需要实现,例如 print_class_name) .

【讨论】:

参考。性能:从一个概念更改为另一个概念相当耗时,并且需要进行大量代码更改,因此如果我不必为这两种情况分析完整代码,我会更喜欢

以上是关于模板化函数或带有指向基类的指针的函数的主要内容,如果未能解决你的问题,请参考以下文章

包含指向派生模板类的基类指针的类的赋值运算符和复制构造函数

删除指向子类的指针会调用基类析构函数吗?

在 C++ 中将指向基类的指针传递给派生类的成员函数

为啥指向基类的派生类指针可以调用派生类成员函数? [复制]

关于C++基类、派生类的引用和指针

如何将子类作为期望基类的函数的参数传递,然后将该对象传递给指向这些抽象类对象的指针向量?