Вопрос по unique-ptr, c++11, c++, pointers – Тривиальный необработанный указатель, который самоинициализируется в nullptr в C ++

3

Мне нравятся новые типы указателей в C ++ 11, но иногда мне все еще нужен необработанный указатель. Однако то, что меня все больше огорчает из-за «сырых» типов в C ++, это их привычка инициализироваться как неопределенная, если ей не дано явное значение. Поскольку я чаще использую std :: shared_ptr <> и тому подобное, эта потребность инициализировать необработанные указатели, чтобы обнулять, становится все более хрупкой и ненужной. Я говорю о:

class foo
{
    ...
    std::shared_ptr< bar > pb;   // Initially null in whatever constructor.
    std::unique_ptr< dar > pd;   // Likewise.
    std::weak_ptr< gar > pg;     // And again.
    lar* pr;                     // Uh-oh! Who knows what this is? Better remember to initialize...
};

foo::foo( int j )
: pr( nullptr )
{...}

foo::foo( const string& s )
: pr( nullptr )
{...}

... etc.: many tedious and error-prone constructor definitions follow.

Поэтому мне бы хотелось получить «необработанный указатель с нулевой инициализацией». Что-то вроде:

class foo
{
    ...
    std::shared_ptr< bar > pb;   // Initially null in whatever constructor.
    std::unique_ptr< dar > pd;   // Likewise.
    std::weak_ptr< gar > pg;     // And again.
    raw_ptr< lar > pr;           // Once more with feeling.
};

foo::foo( int j )
{...}                            // No explicit pointer initialization necessary.

foo::foo( const string& s )
{...}

...

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

Мой вопрос: (1) такая вещь уже существует в стандартной библиотеке? (2) Если нет, то каким был бы самый элегантный / самый маленький способ выполнить реализацию этого типа?

Постскриптум Я не заинтересован в Boost или каких-либо других библиотеках, если, возможно, это библиотека только для заголовков в одном файле. Малость и простота имеют существенное значение.

Это выглядит каккак наилучшим образом использовать неправильный инструмент? тип вопроса. Для начала вам необходимо выяснить, почему для решения вашей реальной проблемы используется необработанный указатель. Если это не оправдано, то, учитывая хрупкость отсутствия инициализации, это не вопрос. David Rodríguez - dribeas
Вы можете просто инициализировать его в классе:lar *pr{};. chris
@jmucchiello Хорошее горе. Является ли такой вопрос действительно правильным местом для обсуждения философии разработки программного обеспечения и использования C ++? Люди используют C ++ разными способами. Живи и давай жить другим! Пожалуйста, просто предположите для целей этого вопроса, что у некоторых программистов C ++ иногда есть веские причины использовать необработанные указатели и хотеть, чтобы они автоматически обнулялись без обращения к инициализации в классе или в конструкторе. OldPeculier
@OldPeculier: компиляторы обычно могут предупреждать вас о неинициализированных членах, поэтому патрулировать их довольно легко ... но вы, в общем, правы, конечно. Я полагаю, классыне должен расти так много, потому что это признак смешивания обязанностей. В частности, если класс содержит необработанные указатели, у него, вероятно, не должно быть много других проблем. Kerrek SB
@OldPeculier: наоборот, необработанные указатели должны быть исключением, а исключение должно использоваться только в нескольких компонентах, к которым нужно обращаться с особой осторожностью. Если вы имеете дело с этим типом специальных компонентов, и вы действительно платите дополнительные шансы, что вы не забудете инициализацию. Проблема возникает, когда вы решаете, что необработанные указатели являются общими, и, привыкнув к ним, вы становитесь более небрежными. В мире, где необработанные указатели редки, код с необработанными указателями уже является подозрительным и с меньшей вероятностью имеет ошибки, которые обходят проверку. David Rodríguez - dribeas

Ваш Ответ

4   ответа
-2

0 чтобы выразить, что указатель еще не инициализирован, значение0 в каком-то смысле это «значение часового» или «волшебное печенье». Думайте о волшебном cookie как о специальном значении, которое указывает состояние, но в остальном неотличимо от нормальных, допустимых значений для этого типа данных.

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

Одним из таких решений является использованиеboost::optional:

typedef Foo* FooPtr;
boost::optional <FooPtr> foo;
// ...
if (!foo)
{
  // the pointer is not yet initialized
} 
else
{
  // the pointer IS initialized
  Foo* theFoo = *foo;
}

Инициализация указателя проста:

foo = new Foo;

optional не является дозорным значением, потому что вы не храните специальное значениеFoo* чтобы указать, что указатель не инициализирован. Единственная работа, котораяoptional означает «да» или «нет» в ответ на вопрос «указатель инициализирован?»

8

вам нужен «Самый тупой умный указатель в мире», который был предложен как дополнение к будущей стандартной библиотеке шаблонов C ++. Вы можете увидеть предложение здесь:«Предложение для самого глупого умного указателя в мире» и здесь:«Предложение для самого глупого в мире Smart Pointer, v2» и здесь:«Предложение для самого глупого в мире Smart Pointer, v3»

Предложение содержит потенциальную частичную реализацию, которую вы можете адаптировать для использования с компилятором, который вы используете в настоящее время. Реализация похожа на решение почти_raw_ptr, предоставленное Марком Рэнсомом. Веб-поиск exempt_ptr даст более подробную информацию.

Предложение для самого глупого в мире интеллектуального указателя, v3, "Переименовано в exempt_ptr to наблюдатель_ptr", смотрите связанный документ с другими изменениями.

exempt_ptr предложение поддерживает хранениеnullptr и имеет конструктор по умолчанию, который позволяет ему выполнять сценарииstd::reference_wrapper не можешь. ChetS
Даже лучше, чем отличный ответ Марка Рэнсома, это точно отражает необходимость, которую я подчеркиваю, обсуждает множество умных потенциальных решений и выделяет сильное. Спасибо. OldPeculier
Я не вижу необходимости в немом указателе, мы можем просто использоватьstd::reference_wrapper, что в основном то же самое. Mooing Duck
13

C ++ 11 допускает в классе инициализацию членов данных.

class foo
{
  // ...
  lar *pr = nullptr;
};

Это всегда будет инициализироватьpr вnullptr если вы не назначите другое значение в конструкторе.

@OldPeculier, убери тег C ++ 11 из вопроса и я с тобой согласен на 100%. Все совместимые наборы инструментов C ++ 11 работают с этим ответом. jmucchiello
@ OldPeculier Я должен поверить на слово. Независимо от того, для чего используются эти необработанные указатели, если они действительно объявлены как члены данных типа указателя, я не понимаю, почему их трудно инициализировать каким-либо значением. Praetorian
Да. Это не помогает в моем конкретном случае, но это правильный ответ. Смотрите комментарий Криса выше с lar * pr {}; - еще аккуратнее. OldPeculier
@OldPeculier Почему это не помогает в вашем случае? Praetorian
Ну, это решает проблему СУХОГО, когда вам нужно только сказатьnullptr один раз, при объявлении, а не повторять его в каждом конструкторе. Это просто не решает, что вам не нужно говорить это вообще. Joe Z
5
template<typename T>
class almost_raw_ptr
{
public:
    almost_raw_ptr(T* p = nullptr) : m_p(p) {}
    T* operator=(T* p) { m_p = p; return p; }
    operator T*() const { return m_p; }
    T* operator->() const { return m_p; }
    T& operator*() const { return *m_p; }
    T& operator[](int i) const { return m_p[i]; }

private:
    T* m_p;
};

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

Да, я представляю, что вещи становятся еще более захватывающими, если вы хотите сделать арифметику с указателями на одной из этих вещей, тоже. Joe Z
@OldPeculier, автоматические преобразования в / из необработанных указателей покроют большинство потребностей в сравнениях и копиях. Mark Ransom
almost_good_answer Walter
Нет. Нужны операторы сравнения, копирующие конструкторы и много другого, если мы собираемся пойти по этому пути. OldPeculier
@jmucchiello, накладные расходы будут не хуже, чем у любого другого умного типа указателя. Встроенное расширение не гарантируется, но стандартные шаблоны библиотеки зависят от него для приемлемой производительности, и этот шаблон ничем не отличается. Посмотрите на определениеstd::vector и скажите мне, что вы найдете дляoperator[]. Mark Ransom

Похожие вопросы