c++前向声明设计

c++ Forward Declaration design

本文关键字:声明 c++      更新时间:2023-10-16

根据我所读到的,我应该尽可能使用前向声明。我有这样的类(其中每个字段都是指针,因为向前声明):

class A
{
    // ...
    A* a;
    B* b;
    C* c;
    D* d;
    E* e;
};

但这有问题。

1-这意味着对构造函数中的每个字段调用new和delete(或者至少用智能指针调用new),而堆栈分配的字段不需要这样做。

2-我读到堆栈分配比堆分配快。

3-这也意味着几乎每个类的每个字段都应该是指针。

我做正确的方式做像我的例子类吗?还是我在远期声明中遗漏了什么?

您所展示的示例是多余的。使用前向声明的建议并不意味着您的代码设计是由前向声明实践驱动的。前向声明只是实现细节,以设计为准。

首先决定是需要聚合还是复合,然后再决定前向声明是否合适。

当前向声明的类型是你的方法签名的一部分,即参数的类型时,#include更喜欢前向声明。

#include "OtherClass.h" // 'bad' practice
class OtherClass; // this is better than #include
....
class MyClass
{
    void method(OtherClass *ptr);
}

这并不是一个绝对的规则,因为使用forward decls代替include并不总是可能/方便的。

含义是相反的——你不应该仅仅为了使用前向声明而使用指针,而应该在你做出设计决定(比如使用指针而不是对象作为成员)之后使用前向声明。

因此,如果使用对象更有意义,那么就这样做,并包含您需要的文件。不要仅仅为了前向声明类而使用指针。

如果使用指针作为成员,最好是前向声明,而不是公开完整的类定义。不要为了满足某些规则而盲目地使用指针。

从技术上讲,如果类的接口不依赖于完全限定类型,则可以(而且应该)使用前向声明。编译器必须为成员保留足够的空间,并在编译时添加管理函数——仅仅在类中使用指针或引用不会引入对类型的依赖。

BTW:前向声明不是那么新:在一些C标准库中,FILE是前向声明的structtypedef,这是有意义的,因为FILE总是用于整个公共文件API中的指针

对不属于类的对象使用指针或引用。但是对于该类拥有的对象,不要使用前向声明作为选择指针的理由。

如果你真的想最小化编译时的依赖关系,考虑使用PIMPL模式,而不是把所有的成员都变成指针:

MyClass.h:

#include <memory>
class MyClassImpl;
class MyClass {
 public:
   MyClass();
  ~MyClass();
   void doThing();
 private:
  std::unique_ptr<MyClassImpl> pimpl_;
};

MyClass.cpp

#include "MyClass.h"
#include "MyClassImpl.h"
MyClass::MyClass() { } // in .cpp so unique_ptr constructor has complete type
MyClass::~MyClass() { } // in .cpp so unique_ptr destructor has complete type
void MyClass::doThing(){
  pimpl_->doThing();
}

MyClassImpl.h:

#include "A.h"
#include "B.h"
class MyClassImpl {
 private:
  A a_;
  B b_;
 public:
  void doThing();
};

MyClassImpl.cpp:

#include "MyClassImpl.h"
void MyClassImpl::doThing() {
  // Do stuff with a_, b_, etc...
}

这可能不会解决性能问题,因为您仍然有动态内存分配,但您必须测量它才能看到。

除了已经给出的好答案之外:在您的类不创建对象但私下使用它的情况下(例如某些实用程序类),可以使用引用代替指针。

class UtilityClass; // forward declaration (even interfaces make sense here)
class MyClass {
public:
    /// takes an UtilityClass for implementing some of its functions
    MyClass(UtilityClass& u): util(u) {}
private:
    UtilityClass& util;
    // ...more details
};

在这些情况下,前向声明并不意味着必须在堆上创建对象(就像您的问题#1和#2一样)。