2015-07-13 3 views
1

Учитывая иерархию классов:Смарт указатель (unique_ptr) вместо сырой указатель в качестве класса элемента

class A { 

private: 
    string * p_str; 

public: 
    A() : p_str(new string()) 
    { 
    } 

    virtual ~A() { 
    delete p_str; 
    } 
}; 

class B : public A { 
public: 
    B() { 
    } 

    virtual ~B() override { 
    } 

    virtual void Test() { 
    cout << "B::Test()" << endl; 
    } 
}; 

int main(int, char**) 
{ 
    B b; 

    b.Test(); 

    return 0; 
} 

Существует p_str указатель на строку (независимо от того, какой объект не указывает на).

Есть ли какие-либо преимущества в замене его на std::unique_ptr за исключением случаев, когда не было написано delete p_str?

class A { 

private: 
    std::unique_ptr<string> p_str; 

public: 
    A() : p_str(make_unique<string>()) 
    virtual ~A() {} 

} 

? Если конструктор любого производного класса выдает исключение, утечка памяти будет происходить в любом варианте кода.

UPD Я попробовал этот код:

включают

#include <memory> 
using namespace std; 

class Pointed { 
public: 
    Pointed() { std::cout << "Pointed()\n"; } 
    ~Pointed() { std::cout << "~Pointed()\n"; } 
}; 

class A1 { 
private: 
    Pointed * p_str; 

public: 
    A1() : p_str(new Pointed()) { 
    cout << "A1()\n"; 
    throw "A1 constructor"; 
    } 

    virtual ~A1() { 
    cout << "~A1()\n"; 
    delete p_str; 
    } 
}; 

class B1 : public A1 { 
public: 
    B1() { 
    throw "B constructor"; 
    } 

    virtual ~B1() override { 
    cout << "~B1()\n"; 
    } 

    virtual void Test() { 
    cout << "B1::Test()" << endl; 
    } 
}; 

class A2 { 
private: 
    std::unique_ptr<Pointed> p_str; 

public: 
    A2() : p_str(make_unique<Pointed>()) { 
    cout << "A2()\n"; 
    throw "A2 constructor"; 
    } 

    virtual ~A2() { 
    cout << "~A2()\n"; 
    } 
}; 


class B2 : public A2 { 
public: 
    B2() { 
    cout << "B2()\n"; 
    throw "B2 constructor"; 
    } 

    virtual ~B2() override { 
    cout << "~B2()\n"; 
    } 

    virtual void Test() { 
    cout << "B2::Test()" << endl; 
    } 
}; 

int main(int, char**) { 
    cout << "B1::A1 (raw pointers)\n"; 

    try { 
    B1 b1; 
    } 
    catch (...) { 
    cout << "Exception thrown for B1\n"; 
    } 

    cout << "\nB2::A2 (unique pointers)\n"; 

    try { 
    B2 b2; 
    } 
    catch (...) { 
    cout << "Exception thrown for b2\n"; 
    } 

    cin.get(); 

    return 0; 
} 

И выход есть:

B1::A1 (raw pointers) 
Pointed() 
A1() 
Exception thrown for B1 

B2::A2 (unique pointers) 
Pointed() 
A2() 
~Pointed() 
Exception thrown for b2 

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

+0

«Если конструктор любого производного класса создает исключение, утечка памяти будет происходить в любом варианте кода». Вы уверены? * Мне кажется, что там [утечка не будет] (http://coliru.stacked-crooked.com/a/0aabaeaa456c99ec). Поскольку это дочерний объект, бросающий исключение в конструкторе, деструктор базового объекта будет вызываться при вызове исключения. – jaggedSpire

+0

@jaggedSpire извините за «из любого производного», но если исключение произошло в конструкторе класса, где определен член, произойдет утечка памяти: http://coliru.stacked-crooked.com/a/3bd94aa1c8fbb7df – vladon

+0

Это правильно , хотя и не то, что вы изначально утверждали. Просто помните, что будьте осторожны с вашей формулировкой - когда дело доходит до исключений, * где * вы бросаете разницу иногда. Поскольку вы исправили свое утверждение, чтобы он не сказал, что будет утечка памяти, если класс inheritor выдает исключение, я бы хотел удалить свой оригинальный комментарий. – jaggedSpire

ответ

7

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

A a; 
A b = a; // b.p_str store the same address, that a.p_str 

С unique_ptr вы не можете копировать/назначить объект, но вы можете переместить его, без записи оператора присваивания/перемещения движение с-TOR.

A a; 
A b = a; // cannot do this, since `unique_ptr` has no copy constructor. 
A b = std::move(a); // all is okay, now `b` stores `unique_ptr` with data and `a` stores `nullptr` 

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

+0

'string' был, например, – vladon

1

В дополнение к НАВСЕГДА это ответ, используя unique_ptr говорит читателю, что есть только один объект со ссылкой на (и в этом случае, владеющим) эту строку. С голым указателем, кто читает ваш код, не знает, сколько других объектов, локалей или что-то еще (одноточие? Глобалы?) Получают доступ и/или изменяют эту строку.

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