Почему я не могу вызвать конструктор моего класса из экземпляра этого класса в C ++? - PullRequest
0 голосов
/ 27 января 2010

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

Например:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 objC.c() ;  // compilation error
}

Ответы [ 10 ]

5 голосов
/ 27 января 2010

По определению, конструктор вызывается только один раз, когда объект создается. Если у вас есть доступ к объекту, значит, он должен быть создан, поэтому вы не можете снова вызывать конструктор - это причина, по которой явные вызовы конструктора не допускаются. Точно так же деструкторы должны вызываться только один раз, когда объект уничтожен. Если бы это всегда можно было сделать автоматически, язык также запретил бы явные вызовы деструкторов.

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

Итак: нет допустимого использования для явных вызовов конструктора, поэтому они недопустимы. Существует явное использование для явных вызовов деструкторов, поэтому они (синтаксически) разрешены, с правилом, что вы должны когда-либо использовать их только на объектах, которые иначе не будут уничтожены, т.е. на объектах, созданных с использованием «размещения нового», и Случай, позвоните им ровно один раз. Использование их любым другим способом, как и многие ошибки C ++, скомпилирует, но даст неопределенное поведение.

4 голосов
/ 27 января 2010

Я думаю, что вы можете явно вызвать деструктор, если убедитесь, что экземпляр заменен / воссоздан с вызовом размещения new:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

int main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 new (&objC) c;  // placement new invokes constructor for the given memory region
}

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

Однако то, что вы, вероятно, хотите, это просто назначение:

objC = c();

Если деструктор имеет побочные эффекты, которые вас интересуют, реализуйте присваивание, используя идиому копирования и обмена, который вызывает деструктор, вызываемый для значения «левой руки».

3 голосов
/ 27 января 2010

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

Это похоже на способ работы new и delete:

int * p = new int;    // call to new needs no existing instance
delete p;             // call to delete requires existing instance

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

2 голосов
/ 27 января 2010

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

#include<new>

class A
{
//members
};

int main()
{
//allocate buffer
char* buffer = new char[sizeof(A)];
//construct A on that memory space
A * ptrToA = ::new (buffer) A();
//destroy the object
ptrToA->~A();
//deallocate the buffer
delete[] buffer;
}

Один случай, когда вы можете найти место для нового использования, это стандарт контейнеры. Распределитель берет на себя ответственность распределять буфер (выделить член) и объекты построены над этим буфер, как они добавляются в контейнер. Например, когда вы делаете зарезервировать для векторного объекта, он резервирует пространство для N объектов, что означает выделяет пространство для N объектов, но не создает их. Тогда, когда Вы делаете push_back и т. д., чтобы добавить элементы, они создаются над этим буфер. По сути, это методика сокращения накладных расходов повторные вызовы функции выделения памяти. И тогда, когда сделано, векторный деструктор уничтожит объекты, вызывающие деструктор явно для всех объектов в нем, а затем вызвать deallocate () функция распределителя для освобождения памяти. Надеюсь, это поможет.

0 голосов
/ 28 января 2010

Кто сказал, что ты не можешь? Вы просто должны знать, как.

void Foo::Bar() {
  *this = Foo(); // Reset *this
}
0 голосов
/ 27 января 2010

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

c objC;
objC.~c();  // force objC's destructor to run
new(&objC); // force objC's constructor to run

Почему разработчики языка не использовали этот синтаксис вместо этого?

c objC;
delete(&objC) c; // force objC's destructor to run
new(&objC) c;    // force objC's constructor to run

Или почему бы и нет:

c objC
objC.~c(); // force objC's destructor to run
objC.c();  // force objC's constructor to run

Мое мнение относительно того, почему они выбрали синтаксис, который они сделали: Первый вариант более гибкий, чем второй. Я могу вызвать конструктор / деструктор по любому адресу, а не только по экземпляру. Эта гибкость необходима для распределения, но не для уничтожения. Кроме того, удаление первой опции кажется очень опасным, когда в беспорядок попадают виртуальные деструкторы.

0 голосов
/ 27 января 2010

Еще один способ думать об ограничении состоит в том, что конструктор является , а не просто другой функцией. Рассмотрим его определение: в отличие от других функций, оно не имеет возвращаемого значения и может иметь список инициализатора. То, что это просто имеет большую часть синтаксиса функции, является своего рода совпадением; он действительно существует только с целью инициализации нового экземпляра объекта.

0 голосов
/ 27 января 2010

Попробуйте это:

obj.ClassName :: ClassName (); // работает в компиляторе VC 6.0

0 голосов
/ 27 января 2010

Вы можете вызвать конструктор класса экземпляра, используя typeof:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles (but is a bad idea)
 typeof objC otherObjC;  // so does this.
}

Это не влияет на значение экземпляра objC, но создает новый экземпляр otherObjC с помощью конструктора класса objC.

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

0 голосов
/ 27 января 2010

Конструктор "c ()" используется с новым, т.е.

c objC = new c();

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

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