Подтвердить что ты не робот

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

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

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

В моем фрагменте bar1 автоматически создается с помощью конструктора по умолчанию (чего я не хочу), а bar2 вызывает ошибку компилятора, потому что вы не можете использовать неинициализированные ссылки (правильно?), а * bar3 счастлив как ларри, потому что указатели могут быть объявлены неинициализированными (кстати, лучше ли установить это значение NULL?).

class Foo
{
public:
    Bar bar1;
    Bar &bar2;
    Bar *bar3;
}

Похоже, я должен использовать указатели в этом сценарии, это правда? Кроме того, какой лучший способ использовать переменную? Синтаксис → немного громоздкий... Жесткая удача? Что относительно умных указателей и т.д.? Это релевантно?

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

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

../src/textures/VTexture.cpp: In constructor ‘vimrid::textures::VTexture::VTexture()’:
../src/textures/VTexture.cpp:19: error: uninitialized reference member ‘vimrid::textures::VTexture::image’

Здесь настоящий код:

// VTexture.h
class VTexture
{
public:
    VTexture(vimrid::imaging::ImageMatrix &rImage);
private:
    vimrid::imaging::ImageMatrix ℑ
}

// VTexture.cpp
VTexture::VTexture(ImageMatrix &rImage)
    : image(rImage)
{
}

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

// VTexture.h
class VTexture
{
public:
    VTexture(vimrid::imaging::ImageMatrix &rimage) : image(rImage) { }
}

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

Фред Ларсон - Да! Существует конструктор по умолчанию; Я пренебрег этим, потому что думал, что это не относится к проблеме (как глупо от меня). После удаления конструктора по умолчанию я вызвал ошибку компилятора, потому что класс используется с std::vector, который требует, чтобы он был конструктором по умолчанию. Таким образом, похоже, что я должен использовать конструктор по умолчанию, и поэтому должен использовать указатель. Позор... или не так ли?:)

4b9b3361

Ответ 1

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

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

Right.


Ответ на вопрос 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, но содержит адрес памяти. Массив является псевдонимом для другой переменной.

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

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

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

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 ℑ
}

Ответ 2

Они могут быть инициализированы. Вам просто нужно использовать список инициализаторов членов.

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

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

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

Ответ 3

Используйте шаблон шаблона нулевого объекта

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

//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;
}

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

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

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

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

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

Ответ 4

Существует также побочный эффект, когда вы определяете, когда вы определяете 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.
}

Ответ 5

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. Это, как правило, очень плохая идея, потому что трудно гарантировать, что время жизни двух объектов будет хорошо скоординировано (т.е. Вы никогда не удаляете переданный объект штриховки перед удалением объекта Foo.)

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

Ответ 6

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

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