Ограничения на использование управляемых типов в C++
К сожалению, существует множество правил, которые ограничивают использование управляемых типов (классов, структур, интерфейсов), что ведет к затруднениям в работе с ними по сравнению с традиционными неуправляемыми типами в C++. Эти правила также оказывают некоторое влияние на то, как управляемый и неуправляемый коды могут взаимодействовать друг с другом.
- Управляемый тип не может быть наследован из неуправляемого типа. С другой стороны, неуправляемый тип не может быть наследован из управляемого типа. Это значит, что структуры иерархий наследственности управляемых и неуправляемых классов всегда отделены друг от друга.
- Управляемые типы не могут иметь друзей (т.е. дружественных функций, классов, структур и интерфейсов). Естественно, данное правило не распространяется на неуправляемые классы C++. Это, конечно, может быть и не так важно для многих программистов, ведь многие рассматривают дружественность как нарушение важной концепции объектно-ориентированного программирования, называемой инкапсуляцией. Неуправляемые типы могут иметь друзей, как и классы в традиционном C++; однако неуправляемые типы могут иметь друзей только из числа неуправляемых типов.
- В отличие от неуправляемых типов, управляемые не поддерживают множественную наследуемость реализации. Однако как управляемые, так и неуправляемые типы поддерживают множественную наследуемость интерфейсов. Возможность наследовать только одну реализацию является еще одним ограничением, с которым могут смириться многие программисты. Несмотря на то, что традиционный C++ поддерживает множественную наследуемость реализаций, большинство объектно-ориентированных языков (в том числе Java и Smalltalk) ее не поддерживают. Даже модель компонентных объектов Microsoft (COM), которая на двоичном уровне основывается на таблице виртуальных функций в стиле C++, не поддерживает эту возможность.
- Управляемый тип может, очевидно, содержать член, который является указателем на управляемый объект. Управляемый тип также может содержать элемент данных, который является неуправляемым объектом или указателем на таковой. С другой стороны, неуправляемый тип не может включать в себя экземпляр управляемого типа или указатель на таковой. Все, что здесь сказано, касается не только указателей, но также и ссылок.
- Неуправляемый класс, в котором не указан явно базовый класс, является независимым корневым классом. В то же время, управляемый класс, в котором не указан явно ни один класс в качестве базового, является производным от корневого класса System::Object (Система::Объект).
- К объекту, участвующему в сборке мусора (т.е. к экземпляру управляемого класса, который использует ключевое слово _gс (сборщик мусора), а не _value (значение) или _поде), можно получить доступ только посредством указателя (или ссылки) на объект в управляемой динамически распределяемой области памяти. Это является отличием от неуправляемых типов, которые могут содержаться либо непосредственно в переменной типа значения, либо к ним можно получить доступ посредством указателя на неуправляемую динамически распределяемую область памяти.
Перечисленные правила использования управляемых типов в C++ подытожены в предлагаемом примере программы. Комментарии помогут вам понять все эти сложные правила. Если вы откроете проект ManagedAndUnmanagedTypes и попробуете раскомментировать каждый из тех операторов (лучше по одному за один проход), который вызывает ошибку компилятора, вы лучше поймете каждое из правил. Всего лишь щелкните на интересующей вас ошибке в окне Task List (Список задач), а затем нажмите F1 для получения документации, в которой разъясняется суть ошибки. И, конечно, двойной щелчок на ошибке в окне Task List (Список задач) приведет вас к соответствующему оператору в окне редактора исходного кода программы.