我正在研究一些C ++,现在我正在与Java对抗它的相似之处。我知道Java中内部类的目的,但是现在我正在尝试在C ++中使用嵌套类,我发现“容器”类的私有属性不是嵌套类的可见性,所以为什么我应该用它们吗?还有,有办法让这些属性可见吗?
答案 0 :(得分:46)
我正在学习一些C ++,现在我正在与Java对抗它的相似之处。
首先要知道C ++嵌套类与静态嵌套类在Java中的类似。 C ++语法中没有任何东西可以重现Java 嵌套类。
我发现内部类不能看到“容器”类的私有属性......
在C ++中,内部类与普通类没有区别,它们不是类成员,因此它们无法访问容器类的私有成员(与其他语言不同)像Java或C#)。
嵌套类是类成员,但对它们可以访问的内容的限制仍然适用(另请参阅本答案末尾的奇怪的事情部分)。它被认为是标准缺陷(参见DR45),然后一些编译器早先实现了C ++ 0x访问规则,即使在编译C ++ 03时也是如此(特别是GCC,感谢Jonathan Wakely发现它)。 / p>
此规则在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之前你不能(当然除非你将它们声明为friend
但是请看下一段),如果你需要这个功能,只需使用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中的嵌套类,但如果nested
是{ container
的朋友。鉴于这个概述的结构:
class container;
class another {
friend class container;
};
class container {
public:
class nested { };
};
nested
可以访问another
的私人成员:
void container::nested::foo(another obj) {
obj.somePrivateMember = 0;
}
这是有效的,因为nested
是 container
的成员,然后友情的传递限制不适用。在C ++ 11之前,将nested
声明为container
的朋友,该代码将无法编译,因为友谊不是传递的。
我们假设我们总是可以声明一个嵌套类作为其容器的朋友?实际标准表示(SO / IEC 14822:2003(E),11.8):
班级的朋友是不属于班级成员的函数或类......
然后我们不应该能够将nested
声明为container
的朋友:在C ++ 03中,嵌套类是类成员(但标准明确表示他们没有访问权限)容器私有,他们也不能成为容器类的朋友)。似乎没有希望,幸运的是大多数编译器允许我们这样做(无论标准是什么)。
答案 1 :(得分:5)
它提供了另一种良好的封装技术。将一个类完全放在另一个类的名称空间中会降低其对代码库其他部分的可见性。这有助于实现可扩展性并减轻您的维护负担。
功能对象通常以这种方式编码。
答案 2 :(得分:1)
不同是不一样的。
Java的内部类创建假定与外部类的对象连接的对象,因此从内部类的方法访问外部类的成员可以在不显式创建指针或引用的情况下完成。 C ++不这样做; 嵌套类只是一个类,其定义嵌套在另一个类的定义中。这对于封装来说很方便,但就是这样:它并不打算神奇地让那种类型的对象知道包含类型的对象。