【问题标题】:C++ - What's the point of nested classes?C++ - 嵌套类的意义何在?
【发布时间】:2015-09-27 09:53:16
【问题描述】:

我正在学习一点 C++,现在我正在与它与 Java 的相似性作斗争。我知道 Java 中内部类的用途,但现在我尝试在 C++ 中使用嵌套类,我发现嵌套类看不到“容器”类的私有属性,所以为什么我应该使用它们吗?另外,有没有办法让这些属性可见?

【问题讨论】:

  • 一个主要的例子是例如一个链表。为什么要将节点结构暴露给世界,而不是它可以是列表类中的私有结构。也就是说,和成员变量、信息隐藏、抽象、通用封装一样。
  • 所以它只是一个封装优点?但是为什么他们选择让私有成员对内部类不可见呢?
  • “我发现“容器”类的私有属性在内部类中是不可见的” 除非你是 2001 年的时间旅行者,否则你错了,或者你需要升级你的编译器是这十年的东西。
  • @JonathanWakely 我是 C++ 新手,所以我不是时间旅行者,也不是知道编译器使用的人。 :)

标签: c++ inner-classes


【解决方案1】:

我正在学习一点 C++,现在我正在与它与 Java 的相似性作斗争。

首先请注意,C++ 嵌套类类似于与 Java 中的静态嵌套类。 C++ 语法中没有任何东西可以重现 Java 嵌套类

我发现内部类看不到“容器”类的私有属性...

C++ 98

在 C++ 中,内部类与普通类没有什么不同,它们不是类成员,因此它们无法访问容器类的私有成员(与其他语言不同像 Java 或 C#)。

C++ 03

嵌套类是类成员,但对它们可以访问的内容的限制仍然适用(另请参阅此答案末尾的 Weird things 部分)。它被认为是一个标准缺陷(参见 DR45),然后一些编译器更早地实现了 C++0x 访问规则,即使在为 C++03 编译时也是如此(尤其是 GCC,感谢 Jonathan Wakely 发现了这一点)。

C++ 11

这个规则在 C++ 11 中改变了,现在嵌套类可以访问容器类的私有成员。从§11.7:

嵌套类是一个成员,因此具有与任何其他成员相同的访问权限。

当然你仍然需要一个实例来访问非静态成员。


...那我为什么要使用它们?

它们是一个实现详细信息,用于对相关类进行分组,并且它们在使用其他语言时可能存在相同的问题(新手清晰,主要) .他们最大的好处是 IMO 是封装,例如你有这个:

class stream {
    virtual void write(const std::string text) = 0;
};

class channel {
public:
    virtual stream* get_stream() = 0;

    // Other methods...
};

class tcp_channel : public channel {
public:
    virtual stream* get_stream() {
        return new tcp_stream(this);
    }

private:
    class tcp_stream : public stream { /* implementation */ };
};

在某些情况下,它们也有助于替换嵌套命名空间:

class protocol {
public:
    virtual void create_connection() = 0;

    class tcp : public protocol { /* implementation */ };
    class shared_memory : public protocol { /* implementation */ };
    class named_pipes: public protocol { /* implementation */ };
};

auto media = protocol::tcp();

或者隐藏实现细节:

class file_system_entry {
public:
    class file : public file_system_entry { };
    class directory : public file_system_entry { };

    std::time_t get_last_modified() { ... }

    void remove() { ... }
    virtual void copy_to(std::string path) = 0;

private:
    class local_handle {
        // Implementation details
    } _handle;
};

还有许多其他的使用模式(另请参阅Why would one use nested classes in C++? 以获得更好的讨论),请记住并非每个人都会正确理解(并使用!)它们。另见Pros and cons of using nested C++ classes and enumerations?

另外,有没有办法让这些属性可见?

在 C++ 11 之前,您不能(当然,除非您将它们声明为 friends,但请参阅下一段),如果您需要此功能,只需使用 C++ 11 编译器(支持此功能)。 GCC(很久以前)和 MSVC 一样,我不知道其他编译器。

嵌套好友

C++ 11 访问规则和友元类之间有什么区别吗?一般来说,它们几乎等价自动访问只是更少详细):

class container {
public:
    class nested;
    friend class nested;

    class nested { };
};

相比:

class container {
public:
    class nested { };
};

但是,使用前向声明会有一些副作用。还要记住,从可访问性的角度来看,它们是等价的(访问,就像友谊一样,不是继承的,也不是传递的)。这些示例无法编译:

class external : public container::nested {
public:
    // No: only class declared inside "container"
    // has access to private members, we do not inherit that 
    void foo(container obj) { /* access a private member of obj*/ }
};

// No, "container" has not access to "nested" private members,
// visibility isn't reciprocal
void container::foo(container::nested obj) {
    // Access some private member of obj
}

// No, we don't have anything to do with container,
// visibility isn't transitive
void friendOfNested(container obj) {
    // Access some private member of obj
}

那么是否完全等价,因为 container 的朋友的私有成员可以在 nested 中访问,如果它是 C++ 11 中的嵌套类,但如果 nestedcontainer 的朋友,则不能访问。鉴于此概述的结构:

class container;

class another {
    friend class container;     
};

class container {
public:
    class nested { };   
};

nested 可以访问another 的私人成员:

void container::nested::foo(another obj) {
    obj.somePrivateMember = 0;
}

之所以有效,是因为nestedcontainer成员,因此友谊的传递限制不适用。在 C++ 11 之前,将 nested 声明为 container 的朋友,该代码将无法编译,因为友谊不具有传递性。

奇怪的事情

我们假设我们总是可以将嵌套类声明为其容器的朋友?其实标准(SO/IEC 14822:2003(E), 11.8):

类的朋友是不是类成员的函数或类...

那么我们不应该能够将nested 声明为container 的朋友:在C++ 03 中,嵌套类是类成员(但标准明确表示它们无权访问容器私有和他们也不能成为容器类的朋友)。似乎没有希望了,幸运的是大多数编译器都允许我们这样做(不管标准如何规定)。

【讨论】:

  • +1,但你不需要标题诡计。没有什么能阻止您将嵌套类维护为包含类的friend
  • 在c++11之前你可以使用friend,不是吗?
  • “这条规则在 C++ 11 中改变了” 不,这条规则是针对 C++03 而不是 C++11 改变的,请参阅DR 45,它被投票加入了工作2001 年的论文。
  • @JonathanWakely 如果我没记错的话,C++03 TC 的最终 (14882) 版本仍然说 “嵌套类的成员没有特殊访问权限...” 并且 GCC 在使用 -std=c++03 进行编译时确实遵守了该规则(我不记得 MSVC,我认为它没有)。 DR45概述问题(C++11后期正式修复)
  • @AdrianoRepetti,你说得对,C++03 中的措辞实际上并没有改变,但 GCC 仍然在 年前 之前在 C++98 模式下实现了这一点(在它甚至没有-std=c++03 选项之前和在解决 DR45 之前,请参阅 2000 年报告的gcc.gnu.org/bugzilla/show_bug.cgi?id=359)。我认为 2.95 是支持 C++98 规则的最后一个版本,这是史前的。
【解决方案2】:

它提供了另一种很好的封装技术。将一个类完全放在另一个类的 命名空间 中会降低它对代码库其他部分的可见性。这有助于实现可扩展性并减轻您的维护负担。

函数对象通常以这种方式编码。

【讨论】:

  • 所以它只是一个封装优点?但是为什么他们选择让私有成员对内部类不可见呢?
  • 查看更好的答案:看起来 C++11 已经放松了这一点。
【解决方案3】:

不同不一样。

Java 的 inner 类创建的对象假定与 outer 类的对象相连,因此可以从内部类的方法访问外部类的成员无需显式创建指针或引用即可完成。 C++ 不这样做。 nested 类只是一个类,其定义嵌套在另一个类的定义中。这对于封装很方便,但仅此而已:它并不是要神奇地使该类型的对象知道包含类型的对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 2011-06-28
    • 2011-05-04
    • 1970-01-01
    相关资源
    最近更新 更多