Serge Hulne
Serge Hulne

Reputation: 614

In C++, can an instance of a class, which does not provide a default constructor, be declared and instantiated separately (in two steps)

In C++, can an instance of a class, which does not provide a default constructor, be declared and instantiated separately (in two steps).

Example:

can

std::istream_iterator<std::string>(fin) istr;

be instanciated as follows:

//declaration
std::istream_iterator<std::string> istr;
// instanciation
istr = *(new std::istream_iterator<std::string>(fin));

Is that correct? or should it be rather done like:

//declaration
std::istream_iterator<std::string> istr;
// instanciation
itPtr = new std::istream_iterator<std::string>(fin);
istr = *itPtr;
//and later: cleaning the temporary allocated object
delete itPrt;

Upvotes: 0

Views: 105

Answers (3)

FdeF
FdeF

Reputation: 552

Assuming you have the following very simple class:

class A {};

then what you can do is:

A a;

or, equivalently:

A a{};

(but not A a(); since this is a forward-declaration of a function whose type is A(void)).
In such cases the default constructor, implicitly generated by the compiler, is invoked and an object of type A is instantiated. Thus, with the above code lines, it is not possible to split declaration and instantiation.

Obviously the following code:

class A{};

int main() {
   A a;
   A* p = new A();
   a=*p;
   delete p;  
   return 0;
}

perfectly compiles. Anyway, when you perform a=*p; then you are actually copying an (unnamed) object (from the free-store) into the object a (previously declared and instantiated into the stack).

Upvotes: 1

David Stone
David Stone

Reputation: 28763

To achieve deferred initialization in C++, you typically want to use boost::optional (or in C++17, std::optional).

optional<your_type> op;
// code
op.emplace(arguments, to, construct, your, type);

This avoids the huge overhead associated with dynamic initialization.

In modern C++, you do not want to ever call new or delete. They lead to memory leaks. First prefer regular stack-allocated objects. Only if that does not work for your should you switch to using a smart pointer like std::unique_ptr (never raw pointers that own memory).

Generally, though, a better pattern than optional (if it can work with your code) is to just declare and construct the variable in one step where it is needed. So the above example would look like:

// some code
auto const value = your_type(arguments, to, construct, your, type);

Or, equivalently,

// some code
your_type const value(arguments, to, construct, your, type);

This completely eliminates the possibility of using an object before it exists. You cannot use the object before it is constructed because there is no way to name the object until it is constructed.

Upvotes: 1

alediaferia
alediaferia

Reputation: 2617

When declaring your instance as:

std::istream_iterator<std::string> istr;

you are asking the compiler to reserve enough "space" on the stack of the program to hold an instance of type std::istream_iterator<std::string>. The data within the instance space will be initialized according to the specified constructor or with the default constructor that applies.

When you do:

std::istream_iterator<std::string> istr;
// instanciation
itPtr = new std::istream_iterator<std::string>(fin);
istr = *itPtr;

you're effectively instiantiating istr and then overwriting it copying the contents from the heap-allocated instance.

One case of "deferred" instantiation is when you want to specify the initialization of a member variable:

class MyClass {
private:
  std::istream_iterator<std::string> istr;

public:
  MyClass() : istr(std::istream_iterator<std::string>(fin)) {}
};

In this case you're specifying how the member variable should be initialized.

Upvotes: 1

Related Questions