станд :: unique_ptr <T[]> API запрещает преобразование указателей на основе базы данных - программирование

станд :: unique_ptr <T[]> API запрещает преобразование указателей на основе базы данных

В Modern Effective C++ "Итерм 19: используйте std::shared_ptr для управления ресурсами совместного использования"., Стр. 133-134, говорится:

std :: shared_ptr поддерживает преобразования указателей на основе базы данных, которые имеют смысл для отдельных объектов, но которые открывают отверстия в системе типов при применении к массивам. (По этой причине API std :: unique_ptr запрещает такие преобразования).

Каково значение "открытых дыр в системе типов"?

Почему std::unique_ptr<T[]> API запрещает преобразование указателей на основе базы данных?

И как он мог запретить конверсии?

4b9b3361

Ответ 1

Отверстие в системе типов происходит всякий раз, когда компилятор не поймает, когда тип передается другому несовместимому типу.

Представьте, что у вас есть два простых класса:

class A
{
    char i;
};

class B : public A
{
    char j;
};

Пусть для простоты игнорируют такие вещи, как padding и т.д., И предполагают, что объекты типа A являются 1 байтом, а объекты типа B - 2 байта.

Теперь, когда у вас есть массив типа A или массив типа B, они будут выглядеть так:

A a[4]:

=================
| 0 | 1 | 2 | 3 |
|-------|-------|
| i | i | i | i |
=================

B b[4]:

=================================
|   0   |   1   |   2   |   3   |
|-------|-------|-------|-------|
| i | j | i | j | i | j | i | j |
=================================

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

a cast to B[4]:

=================================
|   0   |   1   |   2   |   3   |
|-------|-------|-------|-------|
| i | j | i | j | x | x | x | x |
=================================

Первые два объекта в массиве будут интерпретировать i член 2-го и 4-го A как их j элемент. 2-й и 3-й члены получают доступ к нераспределенной памяти.

b cast to A[4]:

=================
| 0 | 1 | 2 | 3 |
|-------|-------|
| i | i | i | i | x | x | x | x |
=================

Здесь это наоборот, все 4 объекта теперь чередуются с интерпретацией i и j экземпляров 2 B как их i член. И половина массива потеряна.

Теперь представьте себе удаление такого литого массива. Какие деструкторы будут называться? Какая память будет освобождена? На данный момент вы находитесь в глубоком аду.

Но подождите, там еще.

Представьте, что у вас есть 3 класса:

class A
{
    char i;
};

class B1 : public A
{
    float j;
};

class B2 : public A
{
    int k;
};

И теперь вы создаете массив указателей B1:

B1* b1[4];

Если вы примените этот массив к массиву указателей A вы можете подумать: "Ну, это хорошо, правильно"?

A** a = <evil_cast_shenanigans>(b1);

Я имею в виду, вы можете безопасно обращаться к каждому члену как указатель на A:

char foo = a[0]->i; // This is valid

Но вы также можете сделать следующее:

a[0] = new B2{};   // Uh, oh.

Это допустимое назначение, компилятор не будет жаловаться, но вы не должны забывать, что мы фактически работаем над массивом, который был создан как массив указателей на объекты B1. И первый член теперь указывает на объект B2, который теперь вы можете получить как B1 без компилятора, говорящего о вещи.

float bar = b1[0]->j;   // Ouch.

Так что снова вы находитесь в глубоком аду, и компилятор не сможет вас предупредить, кроме случаев, когда это восхождение не разрешено в первую очередь.

Почему std :: unique_ptr API запрещает преобразование указателей на основе базы данных?

Я надеюсь, что приведенные выше объяснения дают веские причины.

Как он мог запретить конверсии?

Он просто не предоставляет API для преобразования. API shared_ptr имеет функции преобразования, такие как static_pointer_cast, API unique_ptr этого не делает.