2016-03-14 4 views
0

Я столкнулся с каким-то странным поведением при работе с необработанными указателями и std :: unique_ptr.get(). дал этот пример:Нечетное поведение возврата std :: unique_ptr :: get()

#include <iostream> 
class Car{ 
public: 
    Car(){std::cout << "car gets created\n"; } 
    ~Car(){std::cout << "car gets destroyed\n"; } 
}; 

void func(Car* carPtr){ 
    std::unique_ptr<Car> car = std::make_unique<Car>(); 
    carPtr = car.get(); 
} 

int main(){ 

    Car* carPtr{nullptr}; 
    std::cout << "first check: \n"; 
    if(carPtr){ 
     std::cout << "car Pointer is NOT assigned to nullptr!\n"; 
    } 
    else{ 
     std::cout << "car Pointer is assigned to nullptr\n"; 
    } 

//Variant 1: 
//func(carPtr); 

//Variant 2: 
//std::unique_ptr<Car>car = std::make_unique<Car>(); 
//carPtr = car.get(); 
//car.reset(); 

    std::cout << "\nsecond check: \n"; 
    if(carPtr){ 
     std::cout << "car Pointer is NOT assigned to nullptr!\n"; 
    } 
    else{ 
     std::cout << "car Pointer is assigned to nullptr\n"; 
    } 
    return 0; 
} 

Вариант 1 и Вариант 2 делает в основном ту же вещь: unique_ptr возвращает адрес объекта к указателю, а затем объект будет удален. Однако выход во второй проверке по какой-то причине я не понимаю.

Выход в вариант 1:

first check: 
car Pointer is assigned to nullptr 
car gets created 
car gets destroyed 

second check: 
car Pointer is assigned to nullptr 

Выход в варианте 2 является:

first check: 
car Pointer is assigned to nullptr 
car gets created 
car gets destroyed 

second check: 
car Pointer is NOT assigned to nullptr! 

Я не увидеть разницу. В обоих вариантах я в основном делаю то же самое. Что я упустил?

+0

В чем смысл 'func'? Он не принимает свой параметр по ссылке, поэтому присвоение 'carPtr = ...' в принципе бессмысленно, потому что локальная переменная 'carPtr' уходит, поскольку она выходит за пределы области видимости. – kfsone

ответ

3

В вашей второй «вариант» присвоить ненулевое значение указателя на carPtr:

//Variant 2: 
std::unique_ptr<Car>car = std::make_unique<Car>(); 
carPtr = car.get(); 
car.reset(); 

Это делает carPtr не пустой указатель.

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


Указатель оборванный, относится к никакому объекту, так как объект, который она см был разрушен через car.reset() вызова.


Для того, чтобы создать хороший Неопределенное поведение также для первого варианта,

//Variant 1: 
func(carPtr); 

& hellip; просто изменить сигнатуру функции от

void func(Car* carPtr) 

в

void func(Car*& carPtr) 

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

Теперь вызов функции меняет carPtr на значение висячего указателя, а последующий UB при попытке проверить, является ли он нулевым или нет.

+0

, так что в основном это просто совпадение, основанное на неопределенном поведении, что оба варианта печатают разные выходные данные? – Grundkurs

+0

Нет, не совпадение. UB здесь «слабый» в том смысле, что он вряд ли проявится на современных системах. Это формальная вещь. Но проверка nullness может создать аппаратное исключение в некоторой сегментированной архитектуре. Причина вывода, которую вы видите, также объясняется в ответе. –

+1

@ Гроункурс: Не совсем. Вы можете изменить свой код, чтобы избавиться от неопределенного поведения, но основное различие между вариантами останется. Это принципиальное отличие состоит в том, что первый вариант ** не ** модифицирует 'carPtr' из' main', а второй вариант ** делает ** модифицировать 'carPtr' из' main'. UB, кроме того, здесь. – AnT

2

Ваш первый вариант не имеет ничего общего с вашим вторым.

В первом варианте вы создаете совершенно независимый unique_ptr (местный к функции func), указывая на некоторые полностью независимые объекты Car. Тогда вы уничтожаете их обоих. Ваш carPtr = car.get(); внутри функции в принципе не работает, что ничего не делает.Итак, ваша функция func полностью отделена от чего-либо в main и ничего не влияет на main.

Второй вариант очень отличается. Прямо там, в main, вы создаете unique_ptr, указывая на объект Car. И затем вы найдете get() указатель на этот объект и сохраните его в carPtr от main. Это изменяет значение carPtr в main и изменяет поведение вашей программы.