c++ - 三重继承,没有唯一的最终覆盖

标签 c++ inheritance

我有一个名为 Request 的模板化基类:

template <typename Input, typename Output>
class Request
{
   public:
      // Constructor
      Request(const std::string& request) : request_(request) {}

      // Operation
      virtual Output select(const Input& arg) = 0;

      protected:
         // Attribute 
         const std::string request_;   
};

我还创建了一个类来专门解析要搜索的模板,这只是一个示例,例如来自用户名的电话号码。

// Special request that get phone numbers from usernames
class PhoneRequest : public virtual Request<std::string, std::string>
{
   public:
   protected:
      std::string username_;
};

然后我创建了两个类来搜索两个 SQL 表:

class NationalPhoneRequest : public virtual PhoneRequest
{
    public:
       // Real request that actually get data from the database
       virtual std::string select(const std::string& username)
       {
           username_ = username;
           return call_sql_national(request_, username_);
       }
};

class InterNationalPhoneRequest : public virtual PhoneRequest
{
    public:
        // Real request that actually get data from the database
        virtual std::string select(const std::string& username)
        {
            username_ = username;
            return call_sql_international(request_, username_);
        }
};

然后,因为我想测试我的代码,所以我创建了一个通用的 Fake 类:

template <typename Input, typename Output>
class Fake : public virtual Request<Input, Output>
{
    public:
        // Constructor
        Fake(const std::string& request) : Request<Input, Output>(request) {}  
        // Fake Operation
        virtual Output select(const Input& arg)
        {
            return data_[arg];
        }

        // Add fake data
        void add_data(const Input& key, const Output& data)
        {
            data_[key] = data;
        }

    private:
        // Contains fake data
        std::map<Input, Output> data_;
};

最后,我创建了一个能够模拟 InterNationalPhoneRequest 和 NationalPhoneRequest 的类,因为这两者非常接近。该类(class)也将是假的。我使用关键字“using”来明确指示此类必须使用 Fake 基类方法。

class FakePhoneRequest : public NationalPhoneRequest, public InterNationalPhoneRequest, public Fake<std::string, std::string>
{
    public:
        using Fake<std::string, std::string>::select;
};

我还创建了另一个使用之前所有内容的类:

class Foo
{
    public:
        Foo(NationalPhoneRequest* nat, InterNationalPhoneRequest* internat)
        : nat_(nat)
        , internat_(internat)
        {
        }


        std::string getNatPhoneNumber(const std::string& user)
        {
            return nat_->select(user);
        }

        std::string getInterPhoneNumber(const std::string& user)
        {
            return internat_->select(user);
        }

    private:
        NationalPhoneRequest* nat_;
        InterNationalPhoneRequest* internat_;
};

但是,g++ 失败了:

no unique final overrider for ‘Output Request::select(const Input&) [with Input = std::basic_string; Output = std::basic_string]’ in ‘FakePhoneRequest’ class FakePhoneRequest : public NationalPhoneRequest, public InterNationalPhoneRequest, public Fake

我有几个问题。 1)为什么会失败? 2)我脑海中有一个触发器告诉我我做错了什么,可能是使用了三重继承。

我做对了吗?

最佳答案

您的子类实际上继承了子类中仍然存在的三个完全独立的“选择”功能,“使用”只是让您使用“选择”作为其中之一的简写名称。这样做不会“组合”三个现有的选择功能。

基本上,继承三个具有相同名称的虚函数会为子级提供三个不同的虚函数指针,一个对应于 select 的每个“类型”(因为它们处于不同的层次结构中)。

C++ 不允许这样做,因为没有办法分别正确定义所有三个 select 函数的子版本,因为它们都必须被称为“select”,这会使您的编译器感到困惑.

如果三个选择函数采用不同的参数,则可能不会出现问题。函数的“内部”名称由给定名称和一些定义其采用的参数的标记定义。因此,接受一个字符串的“select”是一个不同的“命名”函数来选择接受两个字符串的函数。只要编译器可以计算出哪个函数是哪个函数,那么重用名称就没有问题。

话虽如此,代码还是有点臭味。任何时候当你对继承相关的事情了解得太深时,几乎总是有更好的方法来做事。一个经验法则是尽量保持继承只有一层深并且基类是抽象的(即不能由它自己实例化)。然后使用指向基类的指针将功能“插入”到不同的类中(它们本身可能是 1-deep 抽象类的子类)。

例如创建一个“PhoneDialler”类,它有一个指向 PhoneRequest 的指针。然后你有一个函数返回指向其中一种 PhoneRequest 类型的指针。这称为工厂方法。然后 PhoneDialler 只需要一个 PhoneRequest 对象并使用它,根本不需要知道 PhoneRequest 的子类。这使得系统更容易扩展新类型。

关于c++ - 三重继承,没有唯一的最终覆盖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42835151/

相关文章:

尝试在 xcode 中退出程序时出现 C++ 11db 错误。初学者级

php - 检查一个类是否是另一个类的子类

ios - 覆盖 super View 中元素的大小?

c# - base() 和 this() 构造函数最佳实践

c++ - 纯虚函数实现

c++ - 难以实现对每行 C++ 上打印的整数数量的限制

c++ - 为什么调用这个构造函数?

c++ - 无法在派生类的子命名空间中实现方法

C++比较冲浪描述符算法匹配有时会中断

javascript - Backbone 中的 Mixin 模式 - 它与 Backbone 的扩展实现有何不同?