2012-07-01 3 views
1

Я не вижу причины, почему они не имеют перегрузки оператора присваивания для простых старых указателей типа, к которому они привязаны. Если цель создания интерфейса интеллектуальных указателей как можно ближе к простым старым указателям, то почему они не сделали перегрузку для оператора присваивания следующим образом?std :: shared_ptr или std :: unique_ptr Перегрузка операторов присваивания

inline std::shared_ptr<type> &operator=(const type * pointer) 
{ 
    reset(a); 
} 

таким образом, вы могли бы использовать их так же, как вы бы нормальный указатель, например, так:

std::shared_ptr<int> test = new int; 

это не проблема вообще, просто интересно, почему они пошли на проблемы просто перегружать пару операторов.

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

Редактировать: добавив ответ Навазу о его ответе здесь для форматирования кода. Я просто написал эту тестовую программу, чтобы увидеть, если то, что вы говорили, был прав:

template<class T> 
class peh 
{ 
public: 
    peh() {meh = 3;} 
    const peh<T> & operator=(const int * peh) 
    { 
    } 
}; 

void f(peh<int> teh) 
{ 

} 

int main() 
{ 
    int * meh = new int; 

    f(meh); 

    system("PAUSE"); 
    return 0; 
} 

это здесь ошибки, говоря, что нет использовать преобразование peh<int> в int *. так почему это приемлемо с std::shared_ptr<int> до int *?

+0

Глобального оператора присваивания нет. Оператор присваивания может быть перегружен только как функция-член. –

+0

@BenjaminLindley: Я думаю, он говорил о том, почему он не является функцией-членом, так как он хочет написать 'std :: shared_ptr test = new int;'. – Nawaz

+0

@Nawaz: Прочтите его последнее заявление. –

ответ

5

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

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

Кстати, если вы хотите использовать следующую функциональность, то вам не следует говорить об операторе присваивания, вы должны спросить: почему конструктор, который принимает необработанный указатель в качестве аргумента, сделан explicit? почему это не подразумевается?

//this code requires an implicit constructor, not assignment! 
std::shared_ptr<int> test = new int; //illegal 

Это незаконно, но предположим, что на некоторое время, что это было разрешено, то вы могли бы вызвать следующую функцию прохождения исходного указателя в качестве аргумента: такая функция будет опасно, как и все остальные из объясняет ответ (читать комментарии):

void f(std::shared_ptr<int> test) 
{ 
    //code 

} //test will be destructed here (when it goes out of scope) 
    //if test.use_count() == 1, then the pointer which it manages 
    //will be destructed as well. (NOTE THIS POINT) 

Теперь посмотрим опасно часть:

int *ptr = new int; 

f(ptr); 
//note that calling f is allowed if it is allowed: 
//std::shared_ptr<int> test = new int; 
//it is as if ptr is assigned to the parameter: 
//std::shared_ptr<int> test = ptr; 

//Question : now what happened in f()? 
//Answer : inside f(), test (the shared_ptr) will infer that no one else 
//refers to the pointer it contains, because test.use_count() == 1 
//test is obviously wrong in this case, because it cannot prove that! 

//DANGER 
*ptr = 10; //undefined behavior, because ptr is deleted by the shared_ptr 

Пожалуйста, ознакомьтесь с комментариями. Он объясняет каждую часть фрагмента кода выше.

+0

Почему бы это не вызвать ошибку void f()? std :: share_ptr и int * - совершенно разные типы. – FatalCatharsis

+0

@FatalCatharsis: потому что это разрешено, если разрешен 'std :: shared_ptr test = new int;'. – Nawaz

+0

@Nawaz: добавлен ответ в моем основном вопросе. – FatalCatharsis

2

operator=, который вы показываете, на самом деле не позволял вам синтаксис. shared_ptr<int> p = new int; будет использовать конструктор shared_ptr из конструктора копирования T * и shared_ptr. shared_ptr имеет оба эти параметра, но ваш синтаксис не работает, потому что конструктор из T * равен explicit.

Причина этого заключается в том, что если эта конструкция, std::shared_ptr<int> test = new int;, может быть выполнена неявно, это означало бы, что shared_ptr может взять на себя ответственность за указатель без каких-либо явных запросов.Nawaz показывает одну причину, по которой это было бы действительно подвержено ошибкам; вам нужно быть очень осторожным, чтобы указатель, который вы используете, не был внезапно принят shared_ptr где-то без вашего ведома, а затем уничтожен из-под вас.

Вот пример, который показывает это опасное неявное строительство:

#include <iostream> 

template<typename T> 
struct owning_pointer { 
    T *t; 
    owning_pointer(T *t) : t{t} {} 
    ~owning_pointer() { 
     std::cout << t << " deleted\n"; 
     delete t; 
    } 
}; 

void foo(owning_pointer<int> thief) {} 

int main() { 
    int *i = new int; 
    std::cout << i << " allocated\n"; 
    foo(i); 
} 

Вывода будет что-то вроде:

0x10d400880 allocated 
0x10d400880 deleted 

И увидеть ошибки вы получаете, когда вы добавляете explicit в owning_ptr-х конструктор. Я получаю:

main.cpp:18:5: error: no matching function for call to 'foo' 
    foo(i); 
    ^~~ 
main.cpp:13:6: note: candidate function not viable: no known conversion from 'int *' to 'owning_pointer<int>' for 1st argument; 
void foo(owning_pointer<int> thief) {} 
    ^

Кроме того, это ненужно, чтобы неявное конструкцию из Т *, так как уже есть некоторые совершенно простые способы выделить без того же потенциала ошибок:

std::shared_ptr<int> test(new int); // one extra character isn't a hardship. I typically prefer() construction anyway. 
std::shared_ptr<int> test{new int}; // although I might start preferring {} construction in C++11 
auto test = std::make_shared<int>(); // this is slightly different in that the allocated int is zero-initialized 

Если вы» инициализировать член shared_ptr, тогда вы можете инициализировать его в списке инициализаторов вместо использования назначения или reset() в теле конструктора:

struct foo { 
    std::shared_ptr<int> m_meh; 
    foo() 
     : m_meh(new int) 
    { 
     // no need for m_meh.reset(new int) here 
    } 
}; 

Что operator= позволил бы это:

shared_ptr<int> s; 
s = new int; 

Это не кажется как ошибка, склонная в неявном строительстве shared_ptr из Т *, но я не могу видеть, что есть на самом деле какое-либо значение для него либо.

+0

Я на самом деле normall конструирую мои указатели, как это, на самом деле, когда он был разделен между объявлением и определением, это заставило меня хотеть это сделать. когда я использовал для объявления указателя-члена в классе ('int * m_meh'), я бы определил в constuctor как' m_meh = new int; ', вы должны объявить вам умный указатель (' std :: shared_ptr m_meh'), а затем в вызове вызова конструктора ('m_meh.reset (new int'), и я предполагаю, что этот синтаксис был просто каким-то образом протирал меня неправильным способом определения без использования оператора присваивания.НО, все работает так же, как я предполагаю. Мне было просто любопытно. – FatalCatharsis

+0

bames53: Даже 's = новый int' подвержен ошибкам. Вы можете легко написать 's = ptr'. И '' может разрушить базовый указатель, когда он выходит из области видимости, и вы все равно можете использовать 'ptr'. – Nawaz

+0

@ Наваз да, но это нигде не так плохо, как наличие shared_ptr, которого вы даже не знаете, существует. Здесь вы, по крайней мере, должны упомянуть shared_ptr, тогда как при неявном построении какой-либо API, который вы используете, может измениться на shared_ptr, и вы никогда не узнаете. – bames53

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