Подтвердить что ты не робот

Неправильно ли разыменовывать указатель, чтобы получить ссылку?

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

Это?

Чтобы уточнить...

MyType *pObj = ...
MyType &obj = *pObj;

Разве это не "грязно", так как вы можете (хотя бы в теории, так как сначала проверите его) разыщите указатель NULL?

EDIT: О, и вы не знаете, были ли объекты динамически созданы или нет.

4b9b3361

Ответ 1

Убедитесь, что указатель не равен NULL, прежде чем вы попытаетесь преобразовать указатель в ссылку и что объект останется в области действия до тех пор, пока ваша ссылка будет (или останется выделенной в отношении кучи) будет в порядке и морально чист:)

Ответ 2

Инициализация ссылки с помощью разыменованного указателя абсолютно прекрасна, ничего плохого в этом не происходит. Если p - указатель, и если разыменование является допустимым (например, оно не равно null), то *p - это объект, на который указывает. Вы можете привязать ссылку к этому объекту так же, как вы привязываете ссылку на любой объект. Очевидно, что вы должны убедиться, что ссылка не переживает объект (как любая ссылка).

Так, например, предположим, что я передал указатель на массив объектов. Это может быть также пара итераторов или вектор объектов или объект map, но я буду использовать массив для простоты. Каждый объект имеет функцию order, возвращающую целое число. Я должен вызвать функцию bar один раз для каждого объекта, чтобы увеличить значение order:

void bar(Foo &f) {
    // does something
}

bool by_order(Foo *lhs, Foo *rhs) {
    return lhs->order() < rhs->order();
}

void call_bar_in_order(Foo *array, int count) {
    std::vector<Foo*> vec(count);  // vector of pointers
    for (int i = 0; i < count; ++i) vec[i] = &(array[i]);
    std::sort(vec.begin(), vec.end(), by_order);
    for (int i = 0; i < count; ++i) bar(*vec[i]); 
}

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

for (int i = 0; i < count; ++i) {
    Foo &f = *vec[i];
    bar(f);
}

Очевидно, что vector<Foo> будет некорректным, так как тогда я буду называть bar копией каждого объекта по порядку, а не по каждому объекту в порядке. bar принимает неконстантную ссылку, так что в стороне от производительности или чего-либо еще, что явно было бы неправильным, если bar изменяет ввод.

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

Ответ 3

Нет. Как еще вы могли бы реализовать operator=? Вы должны разыменовать this, чтобы вернуть ссылку себе.

Обратите внимание, что я все равно сохраняю элементы в контейнере STL по значению - если ваш объект не огромен, накладные расходы на распределение кучи будут означать, что вы используете больше хранилища и менее эффективны, чем вы если вы просто сохранили элемент по значению.

Ответ 4

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

Boost предоставляет библиотеку ptr_container для решения этих ситуаций. Например, ptr_vector внутренне сохраняет указатели на типы, но возвращает ссылки через свой интерфейс. Обратите внимание, что это означает, что контейнер владеет указателем на экземпляр и будет управлять его удалением.

Вот краткий пример, демонстрирующий это понятие.

#include <string>
#include <boost/ptr_container/ptr_vector.hpp>

void foo()
{
    boost::ptr_vector<std::string> strings;

    strings.push_back(new std::string("hello world!"));
    strings.push_back(new std::string());

    const std::string& helloWorld(strings[0]);
    std::string& empty(strings[1]);
}

Ответ 5

Я бы предпочел использовать ссылки везде, но в тот момент, когда вы используете контейнер STL, вам нужно использовать указатели, если вы действительно не хотите передавать сложные типы по значению.

Просто, чтобы быть ясным: контейнеры STL были разработаны для поддержки определенной семантики ( "семантика значений" ), например "элементы в контейнере могут быть скопированы". Поскольку ссылки не являются перегруппированными, они не поддерживают семантику значений (т.е. Пытаются создать std::vector<int&> или std::list<double&>). Вы правы, что не можете помещать ссылки в контейнеры STL.

Как правило, если вы используете ссылки вместо простых объектов, вы либо используете базовые классы, либо хотите избежать нарезки, либо пытаетесь избежать копирования. И да, это означает, что если вы хотите хранить элементы в контейнере STL, вам понадобится использовать указатели, чтобы избежать нарезки и/или копирования.

И да, законно (хотя в данном случае это не очень полезно):

#include <iostream>
#include <vector>

// note signature, inside this function, i is an int&
// normally I would pass a const reference, but you can't add
// a "const* int" to a "std::vector<int*>"
void add_to_vector(std::vector<int*>& v, int& i)
{
    v.push_back(&i);
}

int main()
{
    int x = 5;
    std::vector<int*> pointers_to_ints;

    // x is passed by reference
    // NOTE:  this line could have simply been "pointers_to_ints.push_back(&x)"
    // I simply wanted to demonstrate (in the body of add_to_vector) that
    // taking the address of a reference returns the address of the object the
    // reference refers to.
    add_to_vector(pointers_to_ints, x);

    // get the pointer to x out of the container
    int* pointer_to_x = pointers_to_ints[0];

    // dereference the pointer and initialize a reference with it
    int& ref_to_x = *pointer_to_x;

    // use the reference to change the original value (in this case, to change x)
    ref_to_x = 42;

    // show that x changed
    std::cout << x << '\n';
}

О, и вы не знаете, были ли объекты динамически созданы или нет.

Это не важно. В приведенном выше примере x находится в стеке, и мы сохраняем указатель на x в pointers_to_vectors. Несомненно, pointers_to_vectors использует внутренний динамически выделенный массив (и delete[] этот массив, когда vector выходит за пределы области видимости), но этот массив содержит указатели, а не указания на вещи. Когда pointers_to_ints выходит из области видимости, внутренний int*[] равен delete[] -ed, но int* не delete d.

Это фактически упрощает использование указателей с контейнерами STL, поскольку контейнеры STL не будут управлять временем жизни объектов с указателем. Вы можете посмотреть библиотеку контейнеров указателей Boost. В противном случае вы либо (1) хотите использовать контейнеры STL для интеллектуальных указателей (например, boost:shared_ptr, которые являются законными для контейнеров STL), или (2) управлять другим временем жизни объектов с указателем. Вы уже можете делать (2).

Ответ 6

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

Ответ 7

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

Конечно, то, что происходит в точности, зависит от версии компилятора и аппаратной платформы, а также от параметров компилятора и точного использования ссылки.

Официально поведение разыменования 0-указателя undefined и, следовательно, все может случиться. Это что-то включает в себя то, что он может немедленно сработать, но также и то, что он может сбой значительно позже или никогда.

Поэтому всегда удостоверяйтесь, что вы никогда не назначаете 0-указатель на ссылку - ошибки очень трудно найти.

Изменить: Создал "обычно" курсив и добавил абзац об официальном поведении "undefined".