Я изучаю немного С++, и теперь я борюсь с ним, подобно мне с Java. Я знаю цель внутренних классов в Java, но теперь я пытаюсь использовать вложенные классы в С++, , и я обнаруживаю, что частные атрибуты класса "container" не являются видимыми по вложенному классу, поэтому почему Я должен их использовать? Кроме того, есть ли способ сделать эти атрибуты видимыми?
С++ - Что такое точка вложенных классов?
Ответ 1
Я изучаю немного С++, и теперь я борюсь с ним, соглашаясь с Java.
Прежде всего помните, что вложенные классы С++ похожи на то, что в Java вы называете статическими вложенными классами. В синтаксисе С++ нет ничего, чтобы воспроизводить Java-вложенные классы.
Я обнаружил, что частные атрибуты класса "container" не видны внутренним классом...
С++ 98
В С++ внутренние классы не отличаются от обычных классов, они не являются членами класса, тогда они не могут получить доступ к закрытым членам класса контейнера (в отличие от других языков, таких как Java или С#).
С++ 03
Вложенные классы являются членами класса, но ограничения на доступ к ним все еще применяются (см. также раздел Странные вещи в конце этого ответа). Он считался стандартным дефектом (см. DR45), тогда некоторые компиляторы ранее реализовали правило доступа С++ 0x раньше даже при компиляции для С++ 03 (особенно GCC, благодаря Джонатану Вакели, чтобы это выяснить).
С++ 11
Это правило изменилось в С++ 11, теперь вложенные классы могут получить доступ к частному члену класса контейнера. Из §11.7:
Вложенный класс является членом и как таковой имеет те же права доступа, что и любой другой член.
Конечно, вам все равно нужен экземпляр для доступа к нестационарным членам.
... так почему я должен их использовать?
Затем они являются деталями реализации для группировки связанных классов, и у них одинаковые проблемы с их использованием, которые могут иметься на других языках (ясность для новичков, первичная). Их наибольшая польза от ИМО - инкапсуляция, если, например, у вас есть это:
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;
};
Есть много других шаблонов использования (см. также Почему бы вам использовать вложенные классы в С++? для более удобного обсуждения), просто помните, что не все будут правильно понимать ( и используйте!) их. См. Также Плюсы и минусы использования вложенных классов С++ и перечислений?
Кроме того, есть ли способ сделать видимыми эти атрибуты?
До С++ 11 вы не можете (конечно, если вы не объявите их как friend
, но смотрите следующий параграф), если вам нужна эта функция, просто используйте компилятор С++ 11 (который поддерживает эту функцию). GCC делает (давным-давно), а также MSVC, я не знаю о других компиляторах.
Вложенные друзья
Есть ли разница между правилами доступа С++ 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
, если это вложенный класс в С++ 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
, тогда транзитивное ограничение дружбы не применяется. Прежде чем С++ 11, объявив nested
в качестве друга container
, этот код не будет компилироваться, потому что дружба не является транзитивной.
Странные вещи
Предположим, мы всегда можем объявить вложенный класс как друга своего контейнера? Фактически стандартный указанный (SO/IEC 14822: 2003 (E), 11.8):
Друг класса - это функция или класс, который не является членом класса...
Тогда мы не сможем объявить nested
в качестве друга container
: в С++ 03 вложенные классы являются членами класса (но стандарт явно говорит, что у них нет доступа к контейнерам-рядовым, а также они не могут быть друзьями класса контейнера). Кажется, не было никакой надежды, к счастью, большинство компиляторов разрешили нам это делать (независимо от того, что сказал стандарт).
Ответ 2
Он обеспечивает еще один хороший метод инкапсуляции. Размещение одного класса целиком в пространстве имен другого класса снижает его видимость для других частей вашей базы кода. Это помогает достичь масштабируемости и снижает нагрузку на обслуживание.
Объекты функций часто кодируются таким образом.
Ответ 3
Разные не то же самое.
Java внутренние классы создают объекты, которые предположительно связаны с объектом класса external, поэтому доступ к членам внешнего класса из методов внутреннего класса может быть сделано без явного создания указателя или ссылки. С++ этого не делает; вложенный класс - это просто класс, определение которого вложено в определение другого класса. Это удобно для инкапсуляции, но что это: он не предназначен для волшебного создания объектов этого типа, которые знают об объектах содержащего типа.