我通常几乎不再思考,使用前向声明,这样我就不必包含标题。这个例子的一些东西:

//-----------------------
// foo.h
//-----------------------
class foo
{
   foo();
   ~foo();
};


//-----------------------
// bar.h
//-----------------------

class foo; // forward declaration

class bar
{
   bar();
   ~bar();

   foo* foo_pointer;
};

一些开发人员喜欢使用此方法来避免包含圈子出现问题。我宁愿用它来减少广泛的包含层次结构的开销,这是物理设计的一个重要部分(特别是对于大型项目)。

但是,在某些情况下,我真的想将成员声明为普通对象而不是指针,以便从自动构建/销毁机制中受益。这导致了前向声明不能再使用的问题,因为在这种情况下编译器需要类定义,例如:

//-----------------------
// foo.h
//-----------------------
class foo
{
   foo();
   ~foo();
};


//-----------------------
// bar.h
//-----------------------

class foo;       // Not enough given the way we declare "foo_object"..
#include "foo.h" // ..instead this is required

class bar
{
   bar();
   ~bar();

   foo foo_object;
};

所以,如果有人知道可以在这里使用的替代语言构造,我会很高兴我可以声明<!> quot; foo_object <!> quot;如示例所示,但不包括其标题。

此致

/罗伯特

有帮助吗?

解决方案

只需使用智能指针 - 在这种情况下甚至可以使用auto_ptr。

//-----------------------
// bar.h
//-----------------------

#include <memory>
class foo;       // Not enough given the way we declare "foo_object"..

class bar
{
public:
   bar();
   ~bar();

   foo &foo_object() { return *foo_ptr; }
   const foo &foo_object() const { return *foo_ptr; }

private:
   auto_ptr<foo> foo_ptr;
};

您可以获得自动内存管理的所有好处,而无需在bar.h中了解有关foo的任何信息。有关Herb Sutter的建议,请参见包装指针数据成员

如果你真的希望自动发生默认构造,试试这个:

#include <iostream>
using namespace std;

class Foo;

template <typename T>
class DefaultConstuctorPtr
{
    T *ptr;
    void operator =(const DefaultConstuctorPtr &);
    DefaultConstuctorPtr(const DefaultConstuctorPtr &);

public:
    DefaultConstuctorPtr() : ptr(new T()) {}
    ~DefaultConstuctorPtr() { delete ptr; }

    T *operator *() { return ptr; }
    const T *operator *() const { return ptr; }
};

class Bar
{
    DefaultConstuctorPtr<Foo> foo_ptr;
public:
    Bar() {} // The compiler should really need Foo() to be defined here?
};

class Foo
{
public:
    Foo () { cout << "Constructing foo"; }
};

int main()
{
    Bar bar;
}

其他提示

你做不到。在声明类时,编译器需要知道对象的大小。

参考是一种替代方案,虽然它们必须在施工时进行实例化,因此并不总是可行。

另一种选择是智能指针,但我认为技术上仍然是指针。

很高兴知道为什么你不想使用指针建议其他一些构造虽然......

你想要的东西不能用C ++完成。为了生成对象的代码,您的编译器需要知道它的类需要多少存储空间。为了解这一点,它必须知道每个成员需要多少存储空间。

如果要创建类型为bar的类型为bar的类型,编译器必须知道foo的大小。它知道的唯一方法是它是否有可用的foo定义(通过#include)。否则,您唯一的选择是使用foo的前向声明和指针或引用而不是实际的foo对象。

正如其他人所说,你不能因为他们所说的原因而这样做:)然后你说你不想关心包含它们的类中的成员构造/破坏。您可以使用模板。

template<typename Type>
struct member {
    boost::shared_ptr<Type> ptr;
    member(): ptr(new Type) { }
};

struct foo;
struct bar {
    bar();
    ~bar();

    // automatic management for m
    member<foo> m;
};

我认为代码是自我解释的。如果出现任何问题,请告诉我。

没有办法解决这个问题。

您最好的选择是限制包含的内容,但您必须将该文件包含在类声明中。您可以将类声明拆分为一个单独的标题,希望其中不包含任何其他标题。然后是的,你必须有一个#include,但你仍然保持你的包含层次有点浅。毕竟,包括一个文件是便宜的,只有当层次结构延伸到数百或数千个文件时才开始受到伤害......;)

你唯一可以做的就是通过使用pImpl习语,这样当你包含foo.h时,你只包括foo的界面。

你不能避免包括foo.h,但你可以尽可能便宜。你使用foward声明而不是#inlcudes开发的习惯让你在这条道路上走得很好。

如果您能够使用引用,则可以保留相同的使用语法。但是,您的引用必须在构造函数中立即初始化,因此您的ctor必须绝对定义为out-of-line。 (您还需要在析构函数中释放该对象。)

// bar.h
class foo;

class bar {
    foo& foo_;

public:
    bar();
    ~bar();
};

// bar.cc
bar::bar() : foo_(*new foo)
{
    // ...
}

bar::~bar()
{
    // ...
    delete &foo_;
}

您的里程可能会有所不同。 : - )

您可以使用自定义<!>“智能指针<!>”;自动创建和销毁实例的类。这将实现您所追求的自动构造和破坏。

为了防止需要另一个#include,您可以在项目的前缀标题中包含此myAuto类,或者您可以将其复制并粘贴到每个标题中(不是一个好主意,但它会起作用)。

template<class T>
class myAuto
{
    private:
        T * obj;

    public:
        myAuto() : obj(new T) {  }
        ~myAuto() { delete obj; }
        T& object() { return *obj; }
        T* operator ->() { return obj; }
};

以下是您将如何使用它:

// foo.h:
class foo
{
    public:
        foo();
        ~foo();
        void some_foo_func();
};
//bar.h:
class foo;
class bar
{
    public:
       bar();
       ~bar();
       myAuto<foo> foo_object;
};
//main.cc:
#include "foo.h"
#include "bar.h"

int main()
{
    bar a_bar;

    a_bar.foo_object->some_foo_func();

    return 0;
}

你也可以使用pImpl习语,例如:

//-----------------------
// foo.h
//-----------------------
class foo
{
    foo();
    ~foo();
};


//-----------------------
// bar.h
//-----------------------

class foo;

class bar
{
private:
    struct impl;
    boost::shared_ptr<impl> impl_;
public:
    bar();

    const foo& get_foo() const;
};

//-----------------------
// bar.cpp
//-----------------------
#include "bar.h"
#include "foo.h"

struct bar::impl
{
    foo foo_object;
    ...
}

bar::bar() :
impl_(new impl)
{
}

const foo& bar::get_foo() const
{
    return impl_->foo_object;
}

您仍然可以获得前向声明的好处,另外还可以隐藏您的私有实现。对bar实现的更改不一定需要编译#include bar.h的所有源文件。实现结构本身在.cpp文件中是自包含的,在这里你可以向你的内容声明对象。

由于pImpl本身的影响很小,但是根据应用程序的不同,这可能不是什么大问题。

我已经将pImpl习惯用于大型项目,这对编译时间有很大影响。可惜语言无法处理真正的私有实现,但是你有它。

实际上只有三种方法可以关联两个对象。 你已经发现了两个:在Bar中嵌入Foo,或者把Foo放在堆上,并在一个Foo *中添加一个Foo *。第一个要求在定义类Bar之前定义类Foo;第二个只需要你转发声明类Foo。

确实存在第三种选择,我只提及,因为您明确排除了问题中的前两个选项。您可以(在您的.cpp中)创建一个静态std :: map。在每个Bar构造函数中,您都会向此地图添加一个Foo,键入this。然后,每个酒吧成员都可以通过在地图中查找erase(this)来查找关联的Foo。 Bar :: ~Bar会调用<=>来销毁Foo。

虽然这保持sizeof(Bar)不变,但实际内存使用率高于在Bar中包含Foo *。如果二进制兼容性是一个紧迫的问题,你仍然可以这样做。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top