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

Возможно ли иметь ошибку сегментации от ссылки?

Предположим, что следующий код:

Foo & foo = getFoo();
foo.attr; // 100% safe access?

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

Я пробовал некоторые примеры, например, пытаться использовать NULL для объекта foo, но я получил ошибки компиляции. Я просто хочу быть уверенным, что вышеуказанный код всегда безопасен, и что нет возможной внутренней черной магии C++, о которой я должен знать.

От Benjamin ответьте, я мог бы сделать пример кода, где я получаю ошибку сегментации из ссылки, поэтому он отвечает на мой вопрос. Я вставлю свой код на случай, если кто-то заинтересован в будущем:

#include <iostream>
using namespace std;

class B
{
    public:
    int x;
    B() {x = 5;}
};
class A
{
    public:
    void f()
    {
        b = *(B*)NULL;
    }
    B & getB()
    {
        return b;
    }

    B b;
};

int main()
{
    A a;
    a.f();

    cout << a.getB().x << endl;
    return 0;
}
4b9b3361

Ответ 1

Да, это возможно.

Foo& Fr = *(Foo*)nullptr;

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

Fr.attr = 10;

Однако, как отмечает Джонатан Уэйкли в комментариях, нет причин для проверки такого случая. Если функция возвращает недопустимую ссылку, эта функция прерывается и ее необходимо устранить. Ваш код использования не нарушен, если предположить, что ссылка действительна. Однако действительная ссылка может стать недействительной (хотя и не пустой) в совершенно законном коде, о чем упоминается в ответе Дэвида Шварца. Но вы не можете проверить это. Вам просто нужно знать, в каких случаях это может произойти, а затем прекратить использовать ссылку.

Ответ 2

Ссылка должна ссылаться на действительный объект, когда эта ссылка установлена. Это стандартное требование С++, и любой код, который его нарушает, является UB и может делать буквально все.

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

std::vector<int> j;
j.push_back(3);
int& k = j.front(); // legal, object exists now
j.clear();         // legal, object may be destroyed while reference exists
k++;              // illegal, destruction of object invalidates reference

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

Как правило, вы должны предположить, что это небезопасно, чтобы спрятать возвращаемую ссылку и получить к ней доступ позже, если вы не знаете, что ссылка останется действительной. Например, std::vector тщательно объясняет, при каких условиях ссылка в контейнер может быть признана недействительной и включает последующий вызов push_back. Итак, это сломано:

std::vector<int> j;
j.push_back(3);
int &first = j.front();
j.push_back(4);
int &second = j.back();
if (first == second) // illegal, references into container are invalidated by push_back

Но это прекрасно:

std::vector<int> j;
j.push_back(3);
j.push_back(4);
int &first = j.front();
int &second = j.back();
if (first == second) // legal, references into container stay valid

Ответ 3

Возможно, имеется ссылка на плохую память. И поэтому ответ на foo.attr; // 100% safe access? - нет. Рассмотрим следующий пример:

int &something() {
    int i = 5, &j = i;
    return j; // Return reference to local variable. This is destroyed at end of scope. 
}

int main() {
    int &k = something(); // Equivalent to getFoo()
    std::cout << k << endl; // Using this reference is undefined behavior. 
    return 0;
}

Пример в реальном времени.

Ссылка k не указывает на законную память. Но это все равно будет компилироваться. Нет никакого случая, что это может произойти, однако, когда программист не ошибся. В этом случае функция something() написана неправильно и должна быть исправлена. Нет никакого способа или причины проверить это. Если функция возвращает неверную ссылку, единственное, что вы можете (и должны) сделать, это исправить функцию оскорбления.