如何使用模板正确地重写这个使用继承的C ++代码

时间:2017-06-06 06:28:12

标签: c++ c++11

我有一个C ++代码,目前看起来像这样:有一个类层次结构可以执行一些比较,还有一个使用它的列表类。在运行时基于某个模式对象确定要使用的比较操作。结构如下:

class A{
    bool doComparison(const string& s1, const string& s2) const=0;
}

class B: public A{
    bool doComparison(const string& s1, const string& s2) const {
        ...
    }
}

class C: public A{
    bool doComparison(const string& s1, const string& s2) const {
        ...
    }
}

template <class, S>
public FancyList{
    shared_ptr<A> z_;
    vector<S> v;

    FancyList(shared_ptr<A> z) : z_(z);

    void DoSmth(){
        ....
        z_->doComparison(arg1, arg2);
    }

}

typedef FancyList<string> FancyStringList;

// Determine which comparison to use at runtime   
shared_ptr<A> c = nullptr; 
    switch(type):
        case int: 
          c = make_shared<B>();
          break;
        case double:
          c = make_shared<B>();
          break;
    FancyStringList l(c);
    l.push_back("stuff");

C#曾经是我的主要语言,所以这段代码对我来说似乎没问题。但我被告知这种方法的问题在于它使用虚函数,因此方法调用会有轻微的开销。什么是正确的C ++ - 重新组织此代码的方式,因此不需要具有此类层次结构而不需要使用虚函数?

1 个答案:

答案 0 :(得分:1)

与您想要的相反,虚函数的开销是不可避免的,因为在运行时决定调用哪个实际函数。

如果决定始终在运行时进行,则编译器不能将函数调用硬编码到生成的机器代码中。它必须是间接函数调用:使用指针指向函数,并在函数调用之前取消引用指针。虚函数只是进行间接函数调用的一种方法。

模板是告诉编译器在编译期间生成代码的一种方式。在编译时做出决定时,所有模板都可以不引入开销。它无法帮助您删除必须在运行时完成的工作。

如果您仍然对使用模板感兴趣,可以考虑将比较器作为模板参数。

template <class T, class Comparator>
class MyList
{
    std::vector<T> vec;
    Comparator comp;

public:
    void do_thing(const T& a, const T& b)
    {
        vec.push_back(a);
        vec.push_back(b);
        bool x = comp(vec[0], vec[1]); // for example
        std::cout << x;
    }
};

在比较器类中,重载函数调用操作符

class Compare1
{
public:
    bool operator()(const std::string& lhs, const std::string& rhs) const
    {
        return lhs < rhs;
    }
};

class Compare2
{
public:
    bool operator()(const std::string& lhs, const std::string& rhs) const
    {
        return lhs.size() < rhs.size();
    }
};

int main()
{
    MyList<std::string, Compare1> myli1;
    myli1.do_thing("a", "b");

    MyList<std::string, Compare2> myli2;
    myli2.do_thing("c", "d");
}

您甚至可以隐藏比较器类后面的间接函数调用。但它并没有消除开销。

class A
{
public:
    virtual bool doComparison(const std::string& s1, const std::string& s2) const=0;
    virtual ~A() = default;
};

class PolymorphicComparator
{
private:
    std::shared_ptr<A> comp;
public:
    PolymorphicComp(std::shared_ptr<A> c) : comp(c) {}

    bool operator()(const std::string& lhs, const std::string& rhs) const
    {
        return comp->doComparison(lhs, rhs);
    }
};