указатель на объекты внутри класса, вопрос новичка в C ++ - PullRequest
1 голос
/ 17 марта 2009

почему в C ++, для объектов A, B

//interface, case #1
class A {

B bb;
}

A::A()
{ //constructor
bb = B();
}


//interface, case #2
class A {

B *bb;
}

A::A()
{ //constructor
bb = new B();
}

Почему дело № 2 работает, а не № 1 ??

Редактировать: я получил это сейчас. Но для случая # 1, если экземпляр A освобожден, его bb также будет автоматически освобожден? В случае № 2 вы должны явно вызвать bb = NULL, верно?

Ответы [ 8 ]

4 голосов
/ 17 марта 2009

Оба имеют синтаксические ошибки, поэтому «не работает» не относится ни к одному, ни к другому. Вы также не объявили конструктор A::A, поэтому при попытке его определить вы получите семантическую ошибку.

Если полное объявление B доступно до объявления класса A, оно будет скомпилировано:

class B {};

//interface
class A {
    A();

    B bb;
};

A::A()
{ //constructor
    bb = B();
}

В строке bb=B() bb уже создан, поскольку он является членом A. Затем вы создаете другой B, затем копируете значения этого B в bb. Это немного напрасно. Если вы хотите передать аргументы в конструктор bb, используйте инициализатор.

Если, с другой стороны, вы не хотите помещать определение B перед определением A, а имеете только прямую ссылку на него, компилятор не может определить, насколько велик объект B , Нужно знать, насколько велика буква B, чтобы определить, сколько места можно выделить для bb в A. Поэтому он не сможет скомпилировать A без B.

Во втором случае вместо этого вы используете указатель на B. Указатели имеют одинаковый размер, на что бы они ни указывали. Таким образом, компилятор может определить, насколько велик A, и ему нужно только полное объявление B, прежде чем он фактически его использует, например, вызвав конструктор B в выражении new B().

2 голосов
/ 17 марта 2009

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

2 голосов
/ 17 марта 2009

В C ++, если вы объявляете объект, вам не нужно его инициализировать. Конструктор вызывается при объявлении.

1 голос
/ 17 марта 2009

Если определение (не объявление) класса B появляется перед объявлением класса A, то ваш случай # 1 должен скомпилироваться нормально.

Как указал Оксли , причина этого в том, что C ++ должен знать, сколько памяти требуется для объекта B, и он не может сделать это без определения B.

Однако, если вы используете указатель (как в случае № 2), необходимый объем памяти известен, потому что все указатели занимают одинаковый объем памяти (32-разрядный или 64-разрядный, в зависимости от системы).

Итак, для случая № 2 все, что вам нужно, это объявление B перед определением A. (примечание: объявление, а не определение! Конечно, определение также не повредит, но это не обязательно необходимо) .

Просто чтобы убедиться, что я никого не путаю, (эй, может быть, у меня нет терминологии C ++)

объявление: объявляет о существовании класса или функции, но ничего не говорит о том, что это такое.

, например

class A; //forward declaration

class C
{
   A * a;
   // .....
};

определение: фактически определяет класс или функцию. Хотя для класса он не обязательно определяет его полностью, но он определяет всех его членов.

class A
{
   int a;
   int b;
   void some_method(); //not defined here, only declared, but it's ok
};
1 голос
/ 17 марта 2009

Похоже, ваш класс "B" скрывает operator =. В противном случае то, что вы написали, должно скомпилироваться.

Однако в этом нет необходимости. Это должно работать:

A::A() : bb() { //constructor }

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

A::A() // bb is automatically constructed, since it's a member variable and not a pointer. { }

1 голос
/ 17 марта 2009

Показанный код слишком неполный. Если вы не покажете определение B, боюсь, никто не сможет ответить. Моим диким предположением будет то, что класс B не подлежит копированию (например, закрытый оператор =). С копируемым классом B приведенный вами пример должен работать.

0 голосов
/ 17 марта 2009

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

0 голосов
/ 17 марта 2009

вопрос как по мне было как сделать

A::A()
{ //constructor
bb = new B();
}

но без нового. И я полагаю, что код в вопросе не является реальным кодом. Почему «новое» работает, а простое назначение - нет.

И мой ответ следующий. Если я неправильно понял вопрос или сам ответ неверен - пожалуйста, дайте мне знать.

Изменение

A::A()
{ //constructor
bb = B();
}

до

A::A():
    bb()
{
   // some logic
}

и для согласованности в случае с 'new' лучше реализовать как

A::A():
    bb( new B() )
{
    // some logic
}

В обоих случаях, когда «некая логика» начнет выполнение, вы можете быть уверены, что объект bb либо инициализирован, либо будет сгенерировано исключение.

Чтобы сделать ваш случай компилируемым, В должен внедрить оператор присваивания.

Для редактирования: Ваше предположение о случае 1 верное. в случае 2 вы должны вызвать delete bb в деструкторе класса.

Пожалуйста, оставьте сообщение о причине '-1' . Я действительно смущен.

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