Возможно ли иметь ошибку сегментации от ссылки?
Предположим, что следующий код:
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;
}
Ответы
Ответ 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()
написана неправильно и должна быть исправлена. Нет никакого способа или причины проверить это. Если функция возвращает неверную ссылку, единственное, что вы можете (и должны) сделать, это исправить функцию оскорбления.