Должен ли я использовать указатели для моих полей класса C ++? - PullRequest
13 голосов
/ 10 апреля 2009

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

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

В моем фрагменте bar1 автоматически создается с помощью конструктора по умолчанию (а это не то, что мне нужно), & bar2 вызывает ошибку компилятора, потому что вы не можете использовать неинициализированные ссылки (правильно?), А * bar3 счастлив как larry потому что указатели могут быть объявлены неинициализированными (кстати, лучше ли устанавливать это значение в 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, который требует наличия конструктора по умолчанию. Так что, похоже, я должен использовать конструктор по умолчанию и, следовательно, должен использовать указатель. Позор ... или это? :)

Ответы [ 6 ]

15 голосов
/ 10 апреля 2009

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

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

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

С указателем, чтобы получить доступ к значению по адресу, который он содержит, вам нужно 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 ℑ
}
12 голосов
/ 10 апреля 2009

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

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

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

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

2 голосов
/ 11 апреля 2009

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

Я использую 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. Ноль имеет значащее значение.

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

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

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

1 голос
/ 10 апреля 2009

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

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.
}
0 голосов
/ 10 апреля 2009

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

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

0 голосов
/ 10 апреля 2009
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.)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...