2015-12-21 5 views
24

Пусть следующий код:Возможно ли иметь ошибку сегментации от ссылки?

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

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

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

Из ответа Бенджамина я мог бы привести пример кода, где я получаю segmentation fault из справки, поэтому он отвечает на мой вопрос. Я вставлю свой код на случай, если кто-то заинтересован в будущем:

#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; 
} 
+0

Показать 'функцию getFoo', чтобы лучше ответить на ваш вопрос. –

+0

В моей реальной проблеме 'getFoo' имеет сотни строк кода, а возвращаемый объект может быть изменен сотнями потоков ... Итак, нет, невозможно сделать пример' getFoo'. – Kira

+1

@Kira: Обратите внимание, что ваш segfault почти наверняка из выражения 'b = * (B *) NULL;' в функции 'f()'. Не из 'a.getB(). X' в' main'. –

ответ

28

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

Foo& Fr = *(Foo*)nullptr; 

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

Fr.attr = 10; 

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

+1

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

+1

@DavidSchwartz: Конечно. Я поддержал ваш ответ. –

16

Возможно, имеется ссылка на плохую память. И поэтому ответ 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; 
} 

Live example.

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

+0

У меня такое же возражение против этого ответа, как и у Бежамина. Хотя это правда, это наименее важный случай, о котором нужно знать, потому что это может произойти только в случае ошибки в коде где-то еще. –

+0

@DavidSchwartz Но я уже упоминал об этом? Я не уверен, ясно ли это, но в ответ на это: 'foo.attr; // 100% безопасный доступ? Я верю, что правильно ответил. Нет ... если программист 'getFoo()' совершил ошибку. –

+0

У меня нет проблем с тем, что вы упомянули. Моя проблема связана с тем, что вы не упомянули, - что промежуточный код может сделать действительную ссылку более недействительной. –

23

Ссылка должна ссылаться на действительный объект, когда эта ссылка установлена. Это стандартное требование C++, и любой код, который его нарушает, является 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 
+4

Примечание для OP, невозможно определить, что 'k' недопустимо перед выполнением' k ++ '. Валидность 'k' - это то, что вы должны обеспечить с помощью логики вашей программы в достижении этой точки. –

Смежные вопросы