Должен ли я использовать указатели для моих полей класса C ++?

13
задан Community 23 May 2017 в 21:44
поделиться

6 ответов

Ответ на вопрос 1:

Однако кажется, что это невозможно, поскольку они [ссылки] не могут быть объявлены неинициализированными (верно?).

Верно.


Ответ на вопрос 2:

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

Вы инициализируете ссылки вашего класса в списке инициализатора вашего конструктора:

class Foo
{
public:
    Foo(Bar &rBar) : bar2(rBar), bar3(NULL)
    {
    }

    Bar bar1;
    Bar &bar2;
    Bar *bar3;
}

Ответ на вопрос 3:

В конкретном сценарии, над которым я работаю прямо сейчас, я не хочу использовать нормальные переменные (что правильно кстати, как их называть?)

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

class Foo
{
public: 
  Foo()  : x(0), y(4)
  {
  }

  int x, y;
};

Ответ на вопрос 4:

указатели могут быть объявлены неинициализированными (кстати, это лучшая практика для установить это в NULL?).

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

int *p = NULL;
//...

//Later in code
if(p)
{
  //Do something with p
}

Ответ на вопрос 5:

Похоже, я должен использовать указатели в этом случае это правда? Также, Каков наилучший способ использования переменная?

Вы можете использовать либо указатели, либо ссылки, но ссылки не могут быть переназначены и ссылки не могут быть NULL. Указатель такой же, как и любая другая переменная, например, int, но он содержит адрес памяти. Массив - это псевдоним для другой переменной.

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

Со ссылкой, после того, как она инициализирована и объявлена, вы используете ее так же, как если бы вы использовали переменную, на которую она ссылается. Там нет специального синтаксиса.

С помощью указателя, чтобы получить доступ к значению по адресу, который он содержит, вы должны разыменовать указатель. Вы делаете это, поставив * перед ним.

int x=0;
int *p = &x;//p holds the address of x
int &r(x);//r is a reference to x
//From this point *p == r == x
*p = 3;//change x to 3
r = 4;//change x to 4
//Up until now
int y=0;
p = &y;//p now holds the address of y instead.

Ответ на вопрос 6:

Как насчет умных указателей и т. Д.? Является это уместно?

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

void createANewFooAndCallOneOfItsMethods(Bar &bar)
{
    Foo *p = new Foo(bar);  
    p->f();
    //The memory for p is never freed here, but if you would have used a smart pointer then it would have been freed here.  
}

Ответ на вопрос 7:

Обновление 1:

После попытки реализовать поле ссылочной переменной в моем классе и инициализировать его в конструктор, почему я могу получить следующая ошибка?

Проблема в том, что вы не указали список инициализатора. Смотрите мой ответ на вопрос 2 выше. Все после двоеточия:

class VTexture
{
public:
    VTexture(vimrid::imaging::ImageMatrix &rImage)
      : image(rImage)
    { 
    }
private:
    vimrid::imaging::ImageMatrix ℑ
}
15
ответ дан 2 December 2019 в 05:04
поделиться

Их можно инициализировать. Вам просто нужно использовать список инициализаторов членов.

Foo::Foo(...) : bar1(...), bar2(...), bar3(...)
{
  // Whatever
}

Хорошей идеей будет инициализировать все ваши переменные-члены таким образом. В противном случае, для других типов, кроме примитивных, C ++ все равно инициализирует их конструктором по умолчанию. Назначение их в фигурных скобках - это фактически переназначение, а не их инициализация.

Также имейте в виду, что в списке инициализатора члена указывается, КАК инициализировать переменные члена, а НЕ ЗАКАЗ. Члены инициализируются в порядке их объявления, а не в порядке инициализаторов.

12
ответ дан 2 December 2019 в 05:04
поделиться

Использовать шаблон проектирования пустых объектов

Я использую int s, но это будет то же самое с любым типом.

//header file
class Foo
{
public:
   Foo( void );
   Foo( int& i );
private:
   int& m_int;
};

//source file
static int s_null_Foo_m_i;

Foo::Foo( void ) :
   m_i(s_null_Foo_m_i)
{ }

Foo::Foo( int& i ) :
   m_i(i)
{ }

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

bool Foo::default_constructed( void )
{
   return &m_i == &s_null_Foo_m_i;
}

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

  1. Null имеет значимое значение.

    Этого можно избежать с помощью шаблона проектирования нулевого объекта.

  2. Класс должен быть назначаемым.

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

2
ответ дан 2 December 2019 в 05:04
поделиться

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

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

0
ответ дан 2 December 2019 в 05:04
поделиться

Существует также побочный эффект, когда вы определяете, когда определяете Bar и Bar *

class Foo
{
public:
    Bar bar1; // Here, you create a dependency on the definition of Bar, so the header //file for bar always needs to be included.
    Bar &bar2;
    Bar *bar3; //Here, you create a pointer, and a forward declaration is enough, you don't have to always include the header files for Bar , which is preferred.
}
1
ответ дан 2 December 2019 в 05:04
поделиться
class Foo {
public:
    Bar bar1;
    Bar &bar2;
    Bar *bar3;

    // member bar2 must have an initializer in the constructor
    Bar::Bar(Bar& _bar2) : bar1(), bar2(_bar2), bar3(new Bar()) {}

    Bar::~Bar() {delete bar3;}
}

Обратите внимание, что bar2 не просто инициализируется в ctor; он инициализируется объектом bar , который передается как опорный параметр. Этот объект и поле bar2 будут связаны вместе в течение жизни нового объекта Foo . Обычно это очень плохая идея, потому что трудно гарантировать, что время жизни двух объектов будет хорошо скоординировано (то есть, что вы никогда не избавитесь от переданного объекта bar до удаления Foo object.)

Именно поэтому очень предпочтительно использовать либо переменные экземпляра (как в bar1 ), либо указатели на объекты, расположенные в куче (как в bar3 .)

0
ответ дан 2 December 2019 в 05:04
поделиться
Другие вопросы по тегам:

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