2009-03-07 5 views
54

Что эквивалентно следующему:NULL указатель с boost :: shared_ptr?

std::vector<Foo*> vec; 
vec.push_back(NULL); 

при работе с boost::shared_ptr? Это следующий код?

std::vector< boost::shared_ptr<Foo> > vec; 
vec.push_back(boost::shared_ptr<Foo>()); 

Примечание: Я могу отбросить много таких объектов. Должен ли я объявлять глобальный статический объект nullPtr где-нибудь? Таким образом, должен быть построен только один из них:

boost::shared_ptr<Foo> nullPtr; 
+4

хорошие новости от стандарта на следующий C++: там, вы можете написать "vec.emplace_back();" и получить нулевой указатель append :) –

+7

Рассмотрите возможность использования 'boost :: ptr_vector', для которого требуется меньше накладных расходов. – Philipp

ответ

0

Да, объявите глобальный статический нулевой указатель.

4

Вы можете объявить глобальный nullPtr за shared_ptr<Foo>. Но если вы загрязняете глобальное пространство имен, что бы вы назвали глобальным nullPtr для shared_ptr<Bar>?

Обычно я объявляю нулевой ptr как статичный в классе указателя.

#include <boost\shared_ptr.hpp> 

class Foo; // forward decl 
typedef boost::shared_ptr<Foo> FooPtr; 
class Foo 
{ 
public: 
    static FooPtr Null; 
} 
... 
// define static in cpp file 
FooPtr Foo::Null; 
... 
// use Foo Null 
vec.push_back(Foo::Null); 

Таким образом, каждый класс имеет статический Null.

+1

Вы можете получить более естественный синтаксис, используя шаблонный оператор преобразования для глобальной переменной. См. Мой ответ ... :) –

52

Ваше предложение (вызов конструктора shared_ptr<T> без аргумента) является правильным. (Вызов конструктора со значением 0 эквивалентен.) Я не думаю, что это было бы медленнее, чем вызов vec.push_back() с уже существующим shared_ptr<T>, так как в обоих случаях требуется строительство (прямая конструкция или копирование).

Но если вы хотите «приятнее» синтаксис, вы можете попробовать следующий код:

class { 
public: 
    template<typename T> 
    operator shared_ptr<T>() { return shared_ptr<T>(); } 
} nullPtr; 

Это объявляет один глобальный объект nullPtr, который позволяет следующий естественный синтаксис:

shared_ptr<int> pi(new int(42)); 
shared_ptr<SomeArbitraryType> psat(new SomeArbitraryType("foonly")); 

... 

pi = nullPtr; 
psat = nullPtr; 

Примечание что если вы используете это в нескольких единицах перевода (исходные файлы), вам нужно указать класс имени (например, _shared_null_ptr_type), переместить определение объекта nullPtr в отдельный .cpp-файл и добавить объявления extern в t он заголовочный файл, где определяется класс.

+0

nice идея чувак. я должен был +1, чтобы: p, но обратите внимание на ответ здесь http://stackoverflow.com/questions/395685/how-do-you-choose-between-a-singleton-and-an-unnamed-class/395738# 395738 –

+0

Спасибо, ребята. :) Глядя на сообщение литба, можно предположить, что класс действительно должен быть назван. –

+0

некоторые глупые имена: nullPtrT, NullPtrType, nullPtr_t. независимо от того, что :) я нашел, что они добавляют _t, когда есть только один экземпляр чего-то (например, nothrow_t и nullptr_t). –

17

Ну, это законно:

shared_ptr<Foo> foo; /* don't assign */ 

И в этом состоянии, он не указывает ни на что.Вы даже можете проверить это свойство:

if (foo) { 
    // it points to something 
} else { 
    // no it doesn't 
} 

Так почему бы не сделать это:

std::vector < shared_ptr<Foo> > vec; 
vec.push_back (shared_ptr<Foo>); // push an unassigned one 
1

Вот то, что я думаю, что это немного проще и работает просто отлично

(помните, что typedef это ваш друг):

#include <cstdlib> 
#include <vector> 
#include <iostream> 
#include <boost/shared_ptr.hpp> 

typedef boost::shared_ptr< std::vector<char> > CharVecHandle; 

inline CharVecHandle newCharVec(std::vector<char>::size_type size) { 
    return CharVecHandle(new std::vector<char>(size)); 
} 

inline CharVecHandle newCharVec(void) { 
    return CharVecHandle(); 
} 

int main (void) 
{ 
    CharVecHandle cvh = newCharVec(); 

    if (cvh == NULL) 
     std::cout << "It's NULL" << std::endl; 
    else 
     std::cout << "It's not NULL" << std::endl; 

    std::vector<CharVecHandle> cvh_vec; 

    cvh_vec.push_back(newCharVec(64)); 
    cvh_vec.push_back(newCharVec()); 

    // or call the NULL constructor directly 
    cvh_vec.push_back(CharVecHandle()); 

    return EXIT_SUCCESS; 
} 
+0

Конечно, 'typedef' лучше смотреть, чем' boost :: shared_ptr ', и ОП, похоже, касается только одного типа 'Foo', и в этом случае' typedef' прекрасно вписывается. Если вы хотите избежать распространения 'typedef' для разных типов интеллектуальных указателей, мой подход лучше, я думаю. Не уверен, что вы пытаетесь продемонстрировать своими функциями 'newCharVec(), хотя, можете ли вы объяснить? –

+0

@j_random_hacker: Цель функций 'newCharVec' такая же, как и typedef, удобство и согласованность. Меньше символов для ввода и согласования при создании реальных объектов и объектов NULL. Дело в том, что даже если бы я использовал метод шаблона, я бы, вероятно, создал typedef. Исходя из фона 'C' всякий раз, когда я вижу чрезмерно сложную декларацию типа, мой первый инстинкт - это typedef. Принимая это во внимание, я не уверен, есть ли на самом деле объективная причина выбора одного метода или другого, это может быть проблема стиля или личных предпочтений. –

+0

Ну, одна из причин, почему я предпочитаю мой подход, состоит в том, что, поскольку мой «nullPtr» является агностиком типа, он будет работать в шаблонах функций при манипулировании переменными, тип которых неизвестен, а ваш - нет. Например. Я могу написать шаблон функции 'template push_a_null_ptr (vector > & v) {v.push_back (nullPtr); } ', который будет работать для всех типов' T'. Я признаю, что это не огромный потенциал роста, но я не вижу недостатков. –

9

В C++ 0x вы можете просто конвертировать из nullptr к std::shared_ptr:

std::vector< boost::shared_ptr<Foo> > vec; 
vec.push_back(nullptr); 
Смежные вопросы