【问题标题】:Returning abstract datatypes in C++ without dangling pointers在没有悬空指针的情况下返回 C++ 中的抽象数据类型
【发布时间】:2011-08-08 22:21:14
【问题描述】:

你好,

我来自 C# 背景,没有很多 C++ 经验。为了生成干净的代码,我尝试分离实现和接口,并尽可能使用继承。当我尝试将典型的 C# 概念应用于 C++ 时,我遇到了一个迄今为止我无法解决的问题。我认为这对于一个有经验的 C++ 程序员来说可能是微不足道的,但它已经让我发疯了很长一段时间。

首先我声明一个基类(它现在不包含逻辑,但将来会)

class PropertyBase : public IProperty
{
};

然后我为属性定义一个接口

class IProperty
{
public:
    virtual ~IProperty() {};
    virtual PropertyBase    correct(const ICorrector &corrector) = 0;
    virtual PropertyBase    joinWith(const PropertyBase &partner, const IRecombinator &recombinator) = 0;
};

这就是问题所在:编译器为两个虚函数返回错误,表示不允许声明返回抽象类的函数。当然我不想返回PropertyBase 类型的对象。我想声明其他继承自 PropertyBase 的类,它们返回自己的实例。

现在我读到了一种可能的解决方法是像这样修改IProperty 以返回指针:

class IProperty
{
public:
    virtual ~IProperty() {};
    virtual PropertyBase*   correct(const ICorrector &corrector) = 0;
    virtual PropertyBase*   joinWith(const PropertyBase &partner, const IRecombinator &recombinator) = 0;
};

但是,如果可能的话,我想避免这种情况,以防止内存泄漏。如果有人能更好地解决这个问题,那就太好了。

非常感谢

【问题讨论】:

    标签: c++ class function virtual abstract


    【解决方案1】:

    如果您担心内存泄漏,请切换到智能指针。这具有自我记录 wrt 的额外好处。返回对象的所有权。

    class IProperty
    {
    public:
        virtual ~IProperty() {};
        virtual std::unique_ptr<PropertyBase> correct(const ICorrector &) = 0;
        virtual std::unique_ptr<PropertyBase> joinWith(const PropertyBase &,
                                                       const IRecombinator &) = 0;
    };
    

    在您的客户端代码中:

    std::unique_ptr<PropertyBase> pb(property.correct(corrector));
    // use pb and forget about it; smart pointers do their own cleanup
    

    或者,如果你想对对象进行引用计数:

    std::shared_ptr<PropertyBase> pb(property.correct(corrector));
    

    请参阅 MSDN 文档以获取 unique_ptrshared_ptr

    【讨论】:

      【解决方案2】:

      这可能不是您要寻找的答案,但在我看来,您对 C++ 中的指针和值有点困惑。

      如果你想要适当的临时多态性,你必须在 C++ 中返回一个指针或一个引用。在这种情况下,编译器发出错误,因为基类是抽象的。如果可以实例化一个抽象类,它就会有“漏洞”。

      经验法则是:只要有类层次结构,就不要按值返回此类类型的对象。假设您有class Base { int x; }class Derived : public Base { int y; }。如果你这样做:

      Base Function() { Derived d; return d; }
      ...
      Base b = Function();
      

      那么b 将不是Derived 类“隐藏”Base 的值。 b 的值将是 Base。编译器会“切掉”DerivedBase 之间的差异,并将其放入b

      在 C++ 中,您必须使用指针或引用来促进临时多态性。 C# 中的引用与 C++ 中的指针几乎相同,只是您不必在 C# 中释放对象,因为垃圾收集器会为您处理。

      【讨论】:

        【解决方案3】:

        返回指向对象的指针并没有错。如果您担心内存泄漏,那么解决方案是使用智能指针来存储返回的指针。其中最灵活的是来自boostshared_ptr 或即将推出的C++0x 标准。

        【讨论】:

        • 如果返回指针的函数保证返回一个(指向一个)新对象,我推荐std::unique_ptrshared_ptr 可以从 unique_ptr 构造。
        【解决方案4】:

        更一般地说,如果您要在 C++ 中做大量的工作,那么熟悉指针和内存管理是必不可少的。为了深入了解 C++ 的这些方面和其他棘手的方面,我强烈推荐 Scott Meyers 的 Effective C++ 书籍和 Herb Sutter 的Exceptional C++ 书籍。

        【讨论】:

          【解决方案5】:

          这个问题很容易解决。使用指针或引用作为返回值,但不应在指针中返回所有权,而不应返回所有权。

          例如:

          class A : public Base
          {
          public:
             Base *correct(const I &c) 
               { p2 = do_something(c); return &p2; }
             ...
          private:
             A2 p2;
          };
          

          使这项工作有效的原因是您将 p2 存储在类中,并且永远不会将对象的所有权传递给它之外。接口中的函数不会创建新对象,而是只返回现有对象,并根据函数的参数配置为正确的状态。这是 unique_ptr 和 shared_ptr 解决方案的一个很好的替代方案,后者依赖于堆分配和创建新对象并传递它们。

          现在,这样做的妙招是您需要在类的数据成员中列出您希望从正确() 函数返回的所有可能的类型。例如,如果有时你会返回不同的类型,它看起来像这样:

          class B : public Base
          {
          public:
             Base *correct(const I &c) {
                switch(c.get_bool()) {
                   case false: p3 = do_something_else(c); return &p3;
                   case true: p4 = do_something(c); return &p4;
                };
             }
           private:
              B3 p3;
              B4 p4;
          };
          

          但是将你的对象 p3 和 p4 放在当前 B 对象中将完全解决这个问题。

          【讨论】:

            猜你喜欢
            • 2012-12-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-23
            • 2022-01-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多