在没有悬空指针的情况下在C ++中返回抽象数据类型

时间:2011-04-19 18:44:35

标签: c++ class function virtual abstract

喂,

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

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

class PropertyBase : public IProperty
{
};

然后我为Properties

定义了一个接口
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;
};

但是,如果可能的话,我想避免这种情况,以防止内存泄漏。如果有人能够更好地处理这个问题,那将会很棒。

非常感谢

5 个答案:

答案 0 :(得分:5)

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

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));

请参阅unique_ptrshared_ptr的MSDN文档。

答案 1 :(得分:2)

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

如果你想要适当的ad-hoc多态,你必须在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 ++中,您必须使用指针或引用来促进ad-hoc多态。 C#中的引用与C ++中的指针几乎完全相同,除了你不必释放C#中的对象,因为垃圾收集器会为你处理这个。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

更一般地说,如果你要在C ++中做大量的工作,那么掌握指针和内存管理是很重要的。为了深入介绍C ++的这些以及其他棘手的方面,我强烈推荐Scott Meyers撰写的 Effective C ++ 书籍和Herb Sutter撰写的 Exceptional C ++ 书籍。

答案 4 :(得分:0)

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

例如:

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对象中将完全解决这个问题。