嵌套类 - 将声明与 C++ 中的定义分开

Nested classes - separating the declaration from the definition in C++

本文关键字:定义 C++ 声明 嵌套      更新时间:2023-10-16

是否可以将类声明与其定义分开?当然是这样,但是如果我想在实际定义它之前有一个此类的对象怎么办?请考虑以下示例:

class ID3_Reader{
    public:
        // code omitted for brevity
    protected:
        class Mp3_File;
        Mp3_File mFile;                                             
};

很明显,它不会编译。我必须在ID3_Reader类中定义Mp3_File类。当我只有两节课时,这不是问题。如果像五个呢?我的代码会变得非常混乱。为了避免这个问题,我必须将类声明与其定义分开。我怎样才能做到这一点?请记住,我需要在ID3_Reader类中Mp3_File类的实例。我之所以使用嵌套类,是因为我不希望其他程序员使用 Mp3_File 类。我使用了"protected"关键字,因为我将基于ID3_Reader类创建类。

你可以通过使用指针来实现这一点,就像其他人回答的那样:

class Mp3_File; // forward declaration of class Mp3_File
class ID3_Reader{
    public:
        // code omitted for brevity
    protected:
        Mp3_File *mFile;                                             
};

或者你可以声明class Mp3_File私有的构造函数并声明class Mp3_Fileclass ID3_Reader friend

class Mp3_File {
  Mp3_File() {} // constructor is private
  friend class ID3_Reader;
};
class ID3_Reader{
    public:
        // code omitted for brevity
    protected:
        Mp3_File mFile;                                             
};
auto main() -> int {
    ID3_Reader r;
    Mp3_File m; // Error Mp3_File constructor is private!
  return 0;
}  

因此,其他人将无法使用Mp3_File而您可以在class ID3_Reader范围内使用它。

它不会编译,因为编译器不知道该类将使用多少内存Mp3_File。如果将其更改为指针

class ID3_Reader{
    public:
        // code omitted for brevity
    protected:
        class Mp3_File;
        Mp3_File *mFile;                                             
};

编译得很好(指针具有固定大小 - http://ideone.com/VmmXfK(。

我建议使用指针而不是完整的成员变量,并在 ctor/dtor 中初始化/取消初始化它。

如果不更改"嵌套类"设计,我看不到另一种方法。

你可以通过使用指针来实现这一点。回想一下,尽管您必须有一个完整的类来定义变量,但简单的前向声明非常适合定义指针:

class ID3_Reader{
    public:
        // code omitted for brevity
    protected:
        class Mp3_File;
        Mp3_File *mFile;                                             
};

不幸的是,这使您陷入了管理嵌套类内存的困境,但它确实对外部程序员隐藏了类的所有内部结构。

与其将mFile定义为Mp3_File的实例,不如将其定义为指向Mp3_File的指针。这样,您就不需要知道头文件中的定义。或者更好的是 - 使用智能指针。然后,您需要在类构造函数中使用new创建真实实例,并将其delete ID3_Reader 的析构函数中。

如果您希望继续使用当前语法从类外部访问mFile,请在访问器函数中取消引用它:

Mp3_File& getMp3(){ return *mFile; };

然后 - 如果Mp3_File有一个重载的operator()(或任何其他重载的运算符(,你不需要每次都手动取消引用它。

您可以将类设置为类模板以解决此限制:对于类模板,嵌套类型的定义需要在实例化时可见,而不是在查看类模板的定义时可见。您可能希望使用typedef来实际命名使用的实例化,以避免需要尾随<>。下面是一个快速演示:

 template <typename = void>
 class ID3_ReaderT {
     public:
         // code omitted for brevity
     protected:
         class Mp3_File;
         Mp3_File mFile;                                             
 };
 typedef ID3_Reader<> ID3_Reader;
 template <typename  T>
 class ID3_ReaderT<T>::Mp3_File {
 };
 int main()
 {
     ID3_Reader reader;
 }

当然,这仍然意味着ID3_Reader的每个用户都需要查看嵌套类型的定义。如果你想避免这种情况,你的选择是水平间接的,即使用多个答案已经陈述的指针。