C++ 三重继承,没有唯一的最终重写器

C++ 三重继承,没有唯一的最终重写器,c++,inheritance,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&

我有一个名为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_;   
};
然后,为了测试我的代码,我创建了一个通用的伪类:

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_;
};
但是,g++未能抱怨:

“输出请求::选择(常量输入&)[输入=标准::基本字符串; “FakePhoneRequest”类中的Output=std::basic_string]' FakePhoneRequest:public NationalPhoneRequest,public 国际电话请求,公开伪造

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


我做得对吗?

您的子类实际上继承了三个完全独立的“select”函数,它们仍然存在于子类中,“using”只允许您使用“select”作为其中一个函数的简写名称。这样做不会“组合”现有的三个select函数

基本上,继承三个同名的虚拟函数会给子函数三个不同的虚拟函数指针,每个select的“类型”对应一个指针(因为它们在不同的继承者体系结构中)

C++不允许这样做,因为无法正确地分别定义所有三个select函数的子版本,因为它们都必须被称为“select”,这会让编译器非常困惑

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

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


e、 g.创建一个“PhoneDialler”类,该类具有指向PhoneRequest的指针。然后有一个函数返回一个指向PhoneRequest类型之一的指针。这称为工厂方法。PhoneDialler然后只请求一个PhoneRequest对象并使用它,根本不需要知道PhoneRequest的子类。这使得系统更容易用新类型进行扩展。

您的子类实际上继承了三个完全独立的“select”函数,这些函数仍然存在于子类中,“using”只允许您使用“select”作为其中一个函数的简写名。这样做不会“组合”现有的三个select函数

基本上,继承三个同名的虚拟函数会给子函数三个不同的虚拟函数指针,每个select的“类型”对应一个指针(因为它们在不同的继承者体系结构中)

C++不允许这样做,因为无法正确地分别定义所有三个select函数的子版本,因为它们都必须被称为“select”,这会让编译器非常困惑

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

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


e、 g.创建一个“PhoneDialler”类,该类具有指向PhoneRequest的指针。然后有一个函数返回一个指向PhoneRequest类型之一的指针。这称为工厂方法。PhoneDialler然后只请求一个PhoneRequest对象并使用它,根本不需要知道PhoneRequest的子类。这使得系统更容易用新类型进行扩展。

非常感谢,但是,我担心它会允许您将InternationalPhoneRequest放入一个应包含国家变量的变量中。如果我想在InterNationalPhoneRequest中添加特殊操作,而你的工厂函数处理向你的请求对象发出指针,因此它包含关于哪种类型合适的规则,那该怎么办?我不明白。我需要NationalPhoneRequest和InterNationalPhoneRequest从PhoneRequest派生出来,PhoneRequest本身就是从请求派生出来的。我的FakePhoneRequest必须至少是一个PhoneRequest(以便Foo可以使用它)和一个Fake(以便我可以添加我的特殊数据)。为了进行更严格的输入,我选择了FakePhoneRequest必须继承InternationalPhoneRequest和NationalPhoneRequest,因为我希望不可能将InternationalPhoneRequest放入设计用于包含NationalPhoneRequest的var中。我应该如何使用工厂模式来设计它?尝试导出单独的FakeNationalPhoneRequest和FakeInternationalPhoneRequest。使它们从各自的类和“Fake”继承,但阻止Fake也从请求继承。让派生类处理“fakeness”。使用工厂模式,您可以在其他对象请求项的地方创建一个类或函数,工厂确保它们
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_);
        }
};
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_;
};
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_;
};