shared_ptr и ссылки на C ++ - PullRequest
       30

shared_ptr и ссылки на C ++

7 голосов
/ 10 сентября 2009

Ссылки в C ++ являются условной конструкцией, которая позволяет упростить следующий код C:

f(object *p){
  //do something
}

int main(){
  object* p = (object*) calloc(sizeof(object));
  f(p);
}

до

f(object& o){
  //do something
}

int main(){
  object o = object();
  f(o);
}

Общие указатели - это еще одно удобство в C ++, упрощающее управление памятью. Однако я не уверен, как передать shared_ptr в функцию, подобную f(object& o), которая принимает аргументы по ссылке?

f(object& o){
  //do something
}

int main(){
  shared_ptr<object> p (new object());
  f(*p);
}

Будет ли общий указатель увеличиваться при передаче его объекта по ссылке на функцию?

Ответы [ 4 ]

10 голосов
/ 10 сентября 2009

Возьмите shared_ptr по значению, и счетчик ссылок увеличится. Это проще, когда вы typedef это:

typedef boost:shared_ptr<object> object_ptr;

void foo(object_ptr obj)
{
    obj->/* stuff*/;
    obj.reset(); //only resets this local copy, that means:
                 // reduce reference count (back to 1), and
                 // set obj to point at null.
}

int main(void)
{
    object_ptr obj(new object());
    foo(obj);
}

Имейте в виду, ссылки являются псевдонимами. Когда вы передаете по ссылке, вы не передаете указатели, копии и т. Д., А накладываете псевдоним на другой объект. (На самом деле они реализованы как указатели):

typedef boost:shared_ptr<object> object_ptr;

void foo(object_ptr& obj)
{
    obj.reset(); // the references was never increased, since a copy has not
                 // been made, this *is* obj in main. so the reference 
                 // goes to 0, and obj is deleted
}

int main(void)
{
    object_ptr obj(new object);
    foo(obj); // after this, obj has been reset!
}

Всегда помните, чтобы быть const правильным, чтобы избежать ошибок:

typedef boost:shared_ptr<object> object_ptr;

void foo(const object_ptr& obj)
{
    obj.reset(); // cannot do! 
}

int main(void)
{
    object_ptr obj(new object);
    foo(obj);
}

Я думаю, что вы должны предпочесть передавать умные указатели в качестве ссылок, когда это возможно, чтобы избежать посторонних приращений и уменьшений (и копий и прочего).

2 голосов
/ 10 сентября 2009

Будет ли общий указатель увеличиваться при передаче его объекта по ссылке на функцию?

Нет, поскольку вы обращаетесь к необработанному указателю и затем передаете его. Вы хотите сделать что-то похожее на это:

f(shared_ptr<object> o){
  //do something
}

int main(){
  shared_ptr<object> p (new object());
  f(p);
}
1 голос
/ 10 сентября 2009
f(object& o){
  //do something
}

int main(){
  shared_ptr<object> p (new object());
  f(*p);
}

Будет ли увеличен общий указатель когда его объект передается по ссылке к функции?

В коде выше - нет. p всегда будет иметь свой счетчик ссылок, равный 1. Вы можете проверить это в отладчике. Счетчик ссылок shared_ptr подсчитывает количество экземпляров shared_ptr, которые указывают на один и тот же объект, он не отслеживает ссылки, которые вы создаете, вызывая operator * (). И это не обязательно - поскольку p гарантированно доживет до конца области, и вызов функции находится в той же самой области (или глубже), p будет присутствовать во время весь вызов f (). Так что все в порядке.

... если только в f вы не берете адрес o и сохраняете где-то, что будет длиться после возврата f . Этого вам следует избегать - передайте shared_ptr, если вам нужно это сделать.

0 голосов
/ 10 сентября 2009

Перво-наперво, с функциональной точки зрения, ссылки в C ++ точно такие же, как указатели. Единственная причина, по которой их добавили в язык, - сделать синтаксис перегрузки операторов более естественным. (Например, чтобы разрешить писать a + b вместо & a + & b)

Ваши примеры кода на C и C ++ абсолютно не эквивалентны. Версия C вашего кода C ++ будет:

f(object *p){
  //do something
}

int main(){
  object o;
  object_constructor(&o);
  f(&o);
  object_destructor(&o);
}

Фактически, это тот код, который концептуально генерирует ваш компилятор C ++.

Относительно вашего второго вопроса: Да, это правильный способ вызова функции f. Счетчик общего указателя не будет увеличен. Фактический указатель на объект будет передан, как если бы вы не использовали shared_ptr. Это безопасно, однако, пока f не делает ничего прикольного. Просто помните, что происходит то же самое, как если бы параметр f взял указатель вместо ссылки. Единственное отличие состоит в том, что компилятор автоматически передает адрес переменной без необходимости явно использовать оператор &.

Лично мне не нравится когда-либо передавать переменные по ссылке (хотя передача по константной ссылке - это нормально). Вместо этого я предпочитаю использовать указатель, поскольку на сайте вызова становится понятнее, что функция, которую мы вызываем, может потенциально изменить свой аргумент (поскольку символ & виден на сайте вызова).

Мир

...