【问题标题】:Implementing create function for Interface class in C++在 C++ 中实现接口类的创建函数
【发布时间】:2014-07-30 15:57:52
【问题描述】:

阅读Effective C++,第 31 条描述了两种方法,通过让客户端使用一个类来最小化重新编译时间,该类只为他们可以调用的函数提供接口。此类将这些函数调用引用到其他执行实际工作的类。

描述的第一个方法是 Handle 类,它使用 pImpl 习惯用法将函数调用转发到实现类,该类包含它正在执行的所有操作的实现。 Handle 类有一个指向这种类型的实例化对象的指针,它的函数调用该对象上的相应函数并返回它们的值。

我遇到的问题是第二个,它是使用具有所有纯虚函数的基类实现的。在这个方法中,基类定义了允许调用的函数,但是它们都被声明为纯虚函数,所以派生类才是真正实现它们的。客户端具有指向派生类对象的基类的指针或引用,并调用这些函数,这些函数映射到派生类的已实现函数。这本书描述了让基类有一个工厂函数,它返回一个指向派生类的指针。从书中的例子来看,如果 Person 是基类,RealPerson 是派生类,客户端应该能够像这样创建一个对象:

std::string name;
Date dateOfBirth;
Address address;

//create an object supporting the Person interface
std::tr1::shared_ptr<Person> pp(Person::create(name, dateOfBirth, address);

当我尝试实现类似的东西,围绕一个 Rational 类构建时,我遇到了问题,但是:

class Rational;

class RationalBase {
public:
    virtual ~RationalBase ();

    static RationalBase* create(int top, int bottom) {
        RationalBase* ptr = new Rational(top, bottom);
        return ptr;
    }

    virtual int getNumerator () const = 0;
    virtual int getDenominator () const = 0;
    virtual void setNumerator (int) = 0;
    virtual void setDenominator (int) = 0;
};

class Rational : public RationalBase {
public:
    Rational (int top, int bottom) : numerator(top), denominator(bottom) {}
    virtual ~Rational () {}

    virtual int getNumerator () const {
        return numerator;
    }

    virtual int getDenominator () const {
        return denominator;
    }

    virtual void setNumerator (int top) {
        numerator = top;
    }

    virtual void setDenominator (int bottom) {
        denominator = bottom;
    }

private:
    int numerator;
    int denominator;
};

g++ 编译器在create 函数内的RationalBase* ptr = new Rational(top, bottom); 行上给了我错误invalid use of incomplete type ‘struct Rational’

谷歌搜索这个错误,我发现this question 解释了这样做的原因是你只能对前向声明的类做很多事情,而创建它们的新实例不是其中之一。但是我不明白Interface类应该如何实现?

【问题讨论】:

    标签: c++ class interface


    【解决方案1】:

    是的,你不能在定义Rational 之前执行RationalBase* ptr = new Rational(top, bottom);,因为编译器还不知道该类型是什么。 (它还不知道要分配多少空间,或者要调用什么构造函数)。就个人而言,我不会为此使用工厂方法。

    我只想让用户写:

    RationalBase* ptr = new Rational(top, bottom);
    

    他们自己,因为这使事情变得相当明显。

    如果你想使用智能指针,那么他们当然也可以这样写。

    std::shared_ptr<RationalBase> ptr(new Rational(top, bottom));
    std::unique_ptr<RationalBase> ptr(new Rational(top, bottom)); // ensure only one pointer to this object
    

    或者在 c++11 中更好:-)

    auto ptr = std::make_shared<Rational>(top, bottom); // C++11
    auto ptr = std::make_unique<Rational>(top, bottom); // C++14 only
    

    如果你真的想使用工厂,那么你可以稍微改变一下事情的顺序,将 create 函数移到底部:

    class Rational;
    
    class RationalBase {
    public:
        virtual ~RationalBase () {} // you needed to provide a body to this function
    
        static RationalBase* create(int top, int bottom);
    
        virtual int getNumerator () const = 0;
        virtual int getDenominator () const = 0;
        virtual void setNumerator (int) = 0;
        virtual void setDenominator (int) = 0;
    };
    
    class Rational : public RationalBase {
    public:
        Rational (int top, int bottom) : numerator(top), denominator(bottom) {}
        virtual ~Rational () {}
    
        virtual int getNumerator () const {
            return numerator;
        }
    
        virtual int getDenominator () const {
            return denominator;
        }
    
        virtual void setNumerator (int top) {
            numerator = top;
        }
    
        virtual void setDenominator (int bottom) {
            denominator = bottom;
        }
    
    private:
        int numerator;
        int denominator;
    };
    
    // note that this is defined AFTER the Rational class
    RationalBase* RationalBase::create(int top, int bottom) {
        RationalBase* ptr = new Rational(top, bottom);
        return ptr;
    }
    

    我假设您知道您可以将函数体与它们的声明分开提供。这样做可以让您解决这些类型的“定义顺序”问题。

    【讨论】:

    • @miguel.martin,当然,如果用户知道他们只想要一个指向它的指针。
    • shared_ptrunique_ptr 都不适合,@miguel.martin。为什么在这里使用指针?这没有任何意义。 Rational 显然是一个值。
    • @Evan Teran:谢谢。所有的好方法。我仍然是一名初级程序员,所以我不明白为什么当我按照书上所说的方式做它时它为什么不起作用,我只是想了解是否有一些标准的方式这是应该的待实施。
    • @KonradRudolph:关于它是一个价值,你可能是对的。但是,我不会对此处使用纯虚拟基类和指针的适当性做出判断。我认为这是留给开发人员自己的练习。现在,我只是在解释如何实现模式本身。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2015-12-25
    相关资源
    最近更新 更多