2010-11-24 2 views
2

В настоящее время я использую C++ 0x shared_ptr. У меня есть функция утилиты, которая должна принимать указатель на объект, а функция утилиты выполняет только операцию чтения с объекта, и я могу быть уверен, что объект все еще существует, когда ему нужно разыменовать указатель. Мне интересно, какова наилучшая практика передачи умного указателя до такой функции? Должен ли я передавать интеллектуальный указатель по ссылке, по значению или я могу получить необработанный указатель и передать его функции? Кроме того, какие методы дают лучшую производительность?Передача смарт-указателя вокруг C++

Спасибо.

+0

Единственный способ определения производительности - измерение. Угадать или пытаться вычислить его обычно неправильно, поскольку компилятор выполняет так много оптимизаций, что большая часть кода фактически удаляется. Напишите код меры и оптимизируйте соответствующим образом. – 2010-11-24 08:36:04

ответ

3
  • передачи по значению работает хорошо для меня
  • прохода по ссылке может быть рекомендован кем-то еще, но я не думаю, что стоит
  • получения сырого указателя и передавая это должно быть сделано только для взаимодействия с устаревшим кодом
  • Весь смысл SP заключается в том, что ваш объект умрет только в том случае, если ссылки на него отсутствуют, поэтому, если у вас есть SP под рукой, это означает, что pointee все еще жив (если общие указатели используется правильно)
+0

Я бы дал этому ответу еще один взнос, чтобы отменить анонимный диск - вниз, но я могу голосовать только один раз. :-( – 2010-11-24 08:22:55

+1

@Downvoter: не могли бы вы оставить комментарий, пожалуйста? @Alf: Все в порядке, я уже привык к этому ... Я думаю, что это так, как работает человечество ... не просто проблема SO – 2010-11-24 08:23:09

+8

Я полностью не согласен с пункт 3. Передача исходных указателей является наиболее гибкой, поскольку вы не ставите никаких требований к вызывающему абоненту о том, какую собственность на объект он использует.Передача ссылки может быть лучше, если объект должен существовать, но передача необработанного указателя происходит не только для устаревшего кода. – 2010-11-24 08:28:39

0

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

class A 
{ 
    // implementation 
}; 

void foo(std::shared_ptr<A> p) 
{ 
    // use p 
} 

, но, например, станд :: auto_ptr, вы не можете сделать выше, поскольку объект получает уничтожены:

void boo (std::auto_ptr<A> p) 
{ 
// use p 
} // ops the resource pointer by p gets destructed 

Во 2-м случае , вы должны пройти по ссылке:

void boo (A &p) 
{ 
// use p 
} 
3

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

Только если вам нужно сохранить указатель для последующего использования, нужна ли функция для взаимодействия с вызывающим абонентом о собственности. В этом случае вам необходимо передать shared_ptr, который вы можете сделать по значению или по ссылке const, это не так важно, поскольку shared_ptr дешево копировать. Если вас очень беспокоит производительность, вы можете просмотреть профиль, который лучше.

1

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

void foo(type & t); 
int main() { 
    std::shared_ptr<type> sp(new type); 
    foo(*sp); 
} 

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

3

Вам нужно только передать умный указатель на функцию, если вы имеете в виду также указать право собственности на указатель.

Я недавно написал статью о семантике собственности при передаче std :: unique_ptr в/из функций. Посмотрите http://timoch.com/blog/2013/04/std-unique_ptr-semantic/

Учитывая структуру A:

struct A { int field; }; 

Passing сырой указатель

// you make no statement of ownership 
// the caller stays the owner of the pointed object 
void useA(A* ptr) { 
    // useA simply uses the pointed object 
    // it should not delete it (because it does not own it) 
    ptr->field = 2; 
}  

Передача по значению

std::vector<std::unique_ptr<A>> container; 

// the caller explicitly gives you ownership of the object 
void ownA(std::unique_ptr<A> ptr) { 
    // you can use the pointed object 
    std::cout << ptr->field << std::endl; 

    // the object pointed to by ptr is yours 
    // you can place it in a container that you own 
    container.push_back(std::move(ptr)); 
    // now container owns the pointed A instance 

    // if you do not move it using std::move() 
    // the pointed object is deleted automatically 
} 

Проходя мимо неконстантного ссылки

// the caller is giving you the opportunity to take ownership of the object 
// it is free to not take it though 
void maybeOwnA(std::unique_ptr<A> & ptr) { 
    // ptr is passed by reference 
    // so maybeOwnA can decide based on its own internal 
    // to take ownership of the pointed object 

    // taking ownership requires that std::move is called on ptr 

    // it may also simply use the pointed object and not 
    // take ownership 
} 

void callMaybeOwnA() { 
    std::unique_ptr<A> ptr = new A(); 
    maybeOwnA(ptr); 
    // we know if we still own the instance of A 
    // by looking at ptr 
    if (ptr.get() != nullptr) { 
     // we still own the pointed object 
    } 
} 

Проходя по константной-ссылки

// this is functionally the same as passing a raw pointer 
// but you force the caller to use a std::unique_ptr to call you 
// the caller will not be able to use a std::shared_ptr for example 
// I strongly recommend against this form. Passing a raw pointer is 
// perfectly fine 
void useA(const std::unique_ptr<A> & ptr) { 
    // ptr is const so you cannot call std::move on it 
    // you effectively cannot own it 
} 

// a factory function should return a std::unique_ptr by value 
// the caller owns the pointed object 
std::unique_ptr<A> factory() { 
    return std::unique_ptr<A>(new A()); 
} 

void callFactory() { 
    std::unique_ptr<A> ptr = factory(); 
    // the pointed object is deleted when ptr goes out of scope 
} 

То же самое относится и к станд :: shared_ptr. Если функция просто использует указатель и возвращает, нет никаких оснований передавать std :: shared_ptr. Вам просто нужно передать std :: shared_ptr, когда вы хотите предоставить совместное владение заостренным объектом к компоненту, которому вы звоните. Например, если функция сохранит указатель и ожидает, что сможет использовать его после его возврата (например, при вызове снова).

Надеюсь, это поможет.

TiMoch

0

Ответ очень прост - делать все, что вы будете делать с любым другим классом с относительно дешевой копией счетом.

Примечание: копирование интеллектуальных указателей не так дешево, как копирование целых чисел, например. Вероятно, это происходит между целыми числами и малой std :: string. (Я работал в проекте со всеми умными указателями, отправляемыми по значению, и увеличивал и уменьшал количество ссылок на интеллектуальные указатели, занимая от 4 до 6% времени).

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

Если вы используете ссылку (то, что я лично рекомендую), используйте ссылку на const - таким образом вы разрешите компилятору делать неявный upcast ссылочного класса там, где это необходимо.

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