Классы и структуры ссылки (C++/CX)

C++/CX поддерживает определяемые пользователем ссылочные классы и структуры ссылок, а также определяемые пользователем классы значений и структуры значений. Эти структуры данных являются основными контейнерами, в которых C++/CX поддерживает систему типов среда выполнения Windows. Их содержимое передается в метаданные в соответствии с определенными правилами, что позволяет передавать их между среда выполнения Windows компонентами и универсальная платформа Windows приложениями, написанными на C++ или других языках.

Класс ссылки и структура ссылки имеют следующие основные особенности:

  • они должны объявляться в пространстве имен, в области пространства имен, и в этом пространстве имен они могут иметь режим доступа public (открытый) или private (закрытый). Только открытые типы формируют метаданные; Определения вложенных открытых классов не допускаются, в том числе определения вложенных открытых классов enum . Дополнительные сведения см. в разделах Пространства имен и Видимость типов.

  • Он может содержать в качестве членов C++/CX, включая ссылочные классы, классы значений, структуры ссылок, структуры значений или структуры значений, допускающих значение NULL. Он также может содержать скалярные типы, такие как float64, boolи т. д. Он также может содержать стандартные типы C++, например std::vector , или пользовательский класс при условии, что они не являются открытыми. Конструкции C++/CX могут иметь специальные publicвозможности , protected, internal, privateили protected private . Все члены public или protected передаются в метаданные. Стандартные типы C++ должны иметь доступность private, internalили protected private , что не позволит им быть переданным в метаданные.

  • они могут реализовывать один или несколько классов интерфейсов или структур интерфейсов;

  • они могут наследовать одному базовому классу, а на сами базовые классы налагаются дополнительные ограничения. Наследование в иерархиях открытых классов ссылок имеет больше ограничений, чем наследование закрытых классов ссылок;

  • они не могут объявляться как универсальные. Если они имеют закрытый доступ, они могут быть шаблонами;

  • Их время жизни управляется автоматическим подсчетом ссылок.

Объявление

В следующем фрагменте кода объявляется класс ссылки Person . Обратите внимание, что стандартный тип C++ std::map используется в закрытых членах, а интерфейс среда выполнения Windows IMapView используется в общедоступном интерфейсе. Также обратите внимание, что в конце объявлений ссылочных типов используется оператор "^".

// #include <map>
namespace WFC = Windows::Foundation::Collections;
namespace WFM = Windows::Foundation::Metadata;

[WFM::WebHostHidden]
ref class Person sealed
{
public:
    Person(Platform::String^ name);
    void AddPhoneNumber(Platform::String^ type, Platform::String^ number);
    property WFC::IMapView<Platform::String^, Platform::String^>^ PhoneNumbers
    { 
        WFC::IMapView<Platform::String^, Platform::String^>^ get();
    }
private:
    Platform::String^ m_name;
    std::map<Platform::String^, Platform::String^> m_numbers;
};

Реализация

В этом примере кода демонстрируется реализация класса ссылки Person .

#include <collection.h>
using namespace Windows::Foundation::Collections;
using namespace Platform;
using namespace Platform::Collections;

Person::Person(String^ name): m_name(name) { }
void Person::AddPhoneNumber(String^ type, String^ number)
{
    m_numbers[type] = number;
}
IMapView< String^, String^>^ Person::PhoneNumbers::get()
{
    // Simple implementation. 
    return ref new MapView< String^, String^>(m_numbers);
}

Использование

В следующем примере кода показано, как клиентский код использует класс ссылки Person .

using namespace Platform;

Person^ p = ref new Person("Clark Kent");
p->AddPhoneNumber("Home", "425-555-4567");
p->AddPhoneNumber("Work", "206-555-9999");
String^ workphone = p->PhoneNumbers->Lookup("Work");

Также можно также объявить локальную переменную класса ссылки с помощью семантики стека. Поведение такого объекта аналогично поведению стековой переменной, несмотря на то что память все же выделяется динамически. Важное отличие заключается в том, что нельзя присвоить отслеживаемую ссылку (%) переменной, объявленной с использованием семантики стека: таким образом гарантируется, что счетчик ссылок уменьшается до нуля, когда выполняется выход из функции. В этом примере показаны базовый класс ссылки Uriи функция, в которой он используется с семантикой стека:

void DoSomething()
{
    Windows::Foundation::Uri docs("http://docs.microsoft.com");
    Windows::Foundation::Uri^ devCenter = docs.CombineUri("/windows/");
    // ... 
} // both variables cleaned up here.

Управление памятью

Класс ссылки размещается в динамической памяти с помощью ключевого слова ref new .

MyRefClass^ myClass = ref new MyRefClass();

Оператор ^ handle-to-object называется шляпой и по сути является интеллектуальным указателем C++. Память, на которую он указывает, автоматически освобождается, когда последний дескриптор оказывается вне области или ему явно задается значение nullptr.

По определению класс ссылки имеет семантику ссылки. Когда вы присваиваете значение переменной класса ссылки, копируется не сам объект, а его дескриптор. В следующем примере после операции присваивания оба объекта myClass и myClass2 указывают на одно и то же расположение в памяти.

MyRefClass^ myClass = ref new MyRefClass();
MyRefClass^ myClass2 = myClass;

Когда создается экземпляр класса ссылки C++/CX, его память перед вызовом конструктора инициализируется нулями, поэтому такая инициализация для отдельных членов, включая свойства, не требуется. Если класс C++/CX является производным от класса библиотеки C++ для среды выполнения Windows (WRL), инициализация нулями осуществляется только для области производного класса C++/CX.

Элементы

Класс ссылки может содержать функции-члены public, protectedи private ; только члены public и protected передаются в метаданные. Вложенные классы и классы ссылок разрешены, однако они не могут быть public. Открытые поля не допускаются; открытые члены данных должны объявляться как свойства. Закрытые и защищенные внутренние данные-члены могут быть полями. По умолчанию в классе ссылки доступность всех членов равна private.

Структура ссылки аналогична классу ссылки за тем исключением, что ее члены по умолчанию имеют доступность public .

public Класс ссылки или ссылочные структуры создаются в метаданных, но для использования из других универсальная платформа Windows приложений и среда выполнения Windows компонентов у них должен быть по крайней мере один открытый или защищенный конструктор. Класс ссылки, являющийся открытым и имеющий открытый конструктор, необходимо также объявлять как sealed , чтобы предотвратить его дальнейшее изменение через двоичный интерфейс приложений (ABI).

Открытые члены не могут быть объявлены как , так как const система типов среда выполнения Windows не поддерживает const. Можно использовать статическое свойство для объявления открытого члена данных с постоянным значением.

При определении открытого класса или структуры ссылок компилятор применяет необходимые атрибуты к классу и сохраняет эти сведения в файле WinMD приложения. Однако при определении открытого незамеченного класса ссылки вручную примените Windows::Foundation::Metadata::WebHostHidden атрибут , чтобы класс не был видимым для универсальная платформа Windows приложений, написанных на JavaScript.

Класс ссылки может содержать стандартные типы C++, включая типы const , в любых членах private, internalили protected private .

Открытые классы ссылки, имеющие параметры-типы, не допускаются. Определяемые пользователем универсальные классы ссылок не допускаются. Класс ссылки с атрибутами private, internal или protected private может быть шаблоном.

Деструкторы

В C++/CX вызов delete общедоступного деструктора вызывает деструктор независимо от количества ссылок объекта. Это позволяет определить деструктор, который будет выполнять пользовательскую очистку ресурсов, не относящихся к RAII, детерминированным образом. Однако даже в этом случае сам объект не удаляется из памяти. Память для объекта освобождается, только когда число ссылок достигает нуля.

Если деструктор класса не является общим, он вызывается только в случае, когда число ссылок достигает нуля. При вызове delete для объекта, имеющего частный деструктор, компилятор выдает предупреждение C4493, в котором говорится: "Выражение удаления не оказывает влияния, так как деструктор <имени> типа не имеет "общедоступной" доступности".

Деструкторы классов ссылок можно объявлять только следующим образом:

  • открытый и виртуальный (допускается только для запечатанных и незапечатанных типов);

  • защищенный закрытый и невиртуальный (допускается только для незапечатанных типов);

  • закрытый и невиртуальный (допускается только для запечатанных типов).

Никакие другие сочетания режимов доступа, виртуальности и запечатанности не допускаются. Если деструктор явно не объявлен, компилятор создает открытый виртуальный деструктор, если у базового класса типа или любого из его членов имеется открытый деструктор. В противном случае компилятор создает защищенный закрытый невиртуальный деструктор для незапечатанных типов и закрытый невиртуальный деструктор для запечатанных типов.

При попытке обращения к членам класса, для которого уже запускался деструктор, поведение будет неопределенным; это наиболее вероятная причина сбоя программы. При вызове delete t для типа, у которого нет открытого деструктора, ничего не происходит. При вызове delete this для типа или базового класса, не имеющих деструктора private или protected private в иерархии типов, также ничего не происходит.

При объявлении открытого деструктора компилятор создает код таким образом, чтобы класс ссылки реализовывал интерфейс Platform::IDisposable , а деструктор реализовывал метод Dispose . Platform::IDisposable — это проекция C++/CX для Windows::Foundation::IClosable. Никогда не следует явным образом реализовывать эти интерфейсы.

Наследование

Platform::Object является универсальным базовым классом для всех классов ссылок. Все классы ссылок неявно преобразуются в Platform::Object и могут переопределять Object::ToString. Однако модель наследования среда выполнения Windows не предназначена в качестве общей модели наследования. В C++/CX это означает, что определяемый пользователем открытый ссылочный класс не может служить базовым классом.

При создании пользовательского элемента управления XAML можно использовать в качестве базового класса Windows::UI::Xaml::DependencyObject , если объект участвует в системе свойств зависимостей.

После определения незапечатанного класса MyBase , который наследует классу DependencyObject, другие открытые или закрытые классы ссылок в компоненте или приложении могут наследовать этому классу MyBase. Наследование в открытых классах ссылок должно использоваться только для поддержки переопределений виртуальных методов, полиморфной идентичности и инкапсуляции.

Закрытый базовый класс ссылки не требуется для наследования от существующего незапечатанного класса. Если требуется, чтобы иерархия объектов моделировала собственную структуру программы или обеспечивала повторное использование кода, используйте закрытые или внутренние классы ссылок, а лучше стандартные классы C++. Доступ к функциональности закрытой иерархии объектов можно реализовать с помощью оболочки открытого запечатанного класса ссылки.

Класс ссылки, имеющий открытый или защищенный конструктор в C++/CX, должен быть объявлен как запечатанный. Это ограничение означает, что классы, написанные на других языках, таких как C# или Visual Basic, не могут наследоваться от типов, объявленных в компоненте среда выполнения Windows, написанном на C++/CX.

Ниже приведены основные правила наследования в C++/CX:

  • классы ссылок могут напрямую наследовать не более чем от одного базового класса, но могут реализовывать любое количество интерфейсов;

  • если у класса имеется открытый конструктор, он должен объявляться как запечатанный, чтобы запретить его дальнейшее наследование;

  • можно создавать открытые незапечатанные базовые классы, которые имеют внутренние или защищенные закрытые конструкторы, при условии что базовый класс является производным (прямо или косвенно) от существующего незапечатанного базового класса, такого как Windows::UI::Xaml::DependencyObject. Наследование определяемых пользователем классов ссылок между файлами WinMD не поддерживается; однако класс ссылки может наследовать от интерфейса, определенного в другом файле WinMD. Производные классы можно создавать из определяемого пользователем базового класса ссылки только в одном компоненте среда выполнения Windows или универсальная платформа Windows приложении.

  • для классов ссылок поддерживается только открытое наследование.

    ref class C{};
    public ref class D : private C //Error C3628
    {};
    

В следующем примере показано, как предоставлять доступ к открытому классу ссылки, производному от других классов ссылки в иерархии наследования.

namespace InheritanceTest2 
{
    namespace WFM = Windows::Foundation::Metadata;

    // Base class. No public constructor.
    [WFM::WebHostHidden]
    public ref class Base : Windows::UI::Xaml::DependencyObject
    {
    internal:
        Base(){}
    protected:
        virtual void DoSomething (){}
        property Windows::UI::Xaml::DependencyProperty^ WidthProperty;
    };

    // Class intended for use by client code across ABI.
    // Declared as sealed with public constructor.
    public ref class MyPublicClass sealed : Base
    {
    public:
        MyPublicClass(){}
        //...
    };
}

См. также раздел

Система типов
Классы и структуры значения
Справочник по языку C++/CX
Справочник по пространствам имен