C++ Learning 9 - Constructors and Destructors

1. The constructor is used to initialize the class object, it completes the application for the memory space, assigns the initial value and so on. 
 2. Destructors are mainly used to do cleanup work.

Replenish:
A function or variable name preceded by " :: " but no class name indicates that this is a global variable or public function and does not belong to any namespace. That's all.

1. Why don't constructors and destructors return values?
Constructors and destructors are two very special functions: they don't return a value. This is clearly different from functions that return void, which don't return any value, but allow it to do something else, which constructors and destructors don't allow. The behavior of creating and destroying an object in a program is very specific, like birth and death, and it is always up to the compiler to call these functions to ensure they are executed. If they have return values, either the compiler must know what to do with the return value, or the client programmer must explicitly call the constructor and destructor themselves, thus breaking security. Also, the destructor takes no parameters, because destructors don't require any options.
If constructors were allowed to return values, in some cases, ambiguity would arise. The following two examples
class C
{
public:
C(): x(0) { }
C(int i): x(i) { }

private:
int x;
};

If a C constructor could have a return value, such as int: int C():x( 0 ) { return  1 ; } // 1 means the construction succeeded, 0 means failure 
What would happen to the following code?
C c = C();   // now cx == 1! ! ! 
Clearly, C() calls C's parameterless constructor. The constructor returns the int value 1. C happens to have one but parameter constructor C( int i ). So here comes the confusion. According to the provisions of C++, C c = C(); uses the default constructor to create a temporary object and initializes c with this temporary object. At this point, the value of cx should be 0. However, if C::C() has a return value and returns 1 (to indicate success), C++ will initialize c with 1, that is, call the parameter constructor C::C( int i). The resulting cx will be 1. Thus, semantic ambiguity arises. Makes C++ 's already very complex syntax, further confusing.
The reason why the call of the constructor does not set a return value is because of the particularity of the constructor. From a basic semantic point of view, a constructor should return the constructed object. Otherwise, we won't be able to use temporary objects:
void f( int a) {...}   // (1) 
void f( const C& a) {...} // (2) 
f(C()); // (3), who to call? 
For ( 3 ), we want to call ( 2 ), but if C::C() has a return value of type int, then it is better to call ( 1 ) or call ( 2 ). As a result, our overloading system, and even the entire grammar system, will collapse.
The core here is the type of the expression. Currently, the type of expression C() is class C. But if C::C() has a return type R, then the type of the expression C() should be R, not C, thus causing the type problem described above.

2. Explicitly call the constructor and destructor
#include <iostream>
using namespace std;

class MyClass
{
public:
MyClass()
{
cout << "Constructors" << endl;
}

~MyClass()
{
cout << "Destructors" << endl;
}
};

intmain ()
{
MyClass *pMyClass = new MyClass;
pMyClass->~MyClass();
delete pMyClass;

return 0;
}

result:
Constructors
Destructors     // This is the destructor called by the display 
Destructors     // This is the destructor called by delete 
What's the use? Sometimes it comes in handy when you want to end an object before its lifetime ends. Calling the destructor directly does not free the memory where the object resides.
This comes to mind:
When new, it actually does three things, one is: call :: operator new to allocate the required memory. The second is: call the constructor. Three: return a pointer to the newly allocated and constructed object.
When deleting, two things are done, one is: call the destructor, and the other is: call :: operator delete to release the memory.
So presumably the constructor can also be called explicitly. Do an experiment:
intmain ()
{
    MyClass *pMyClass = (MyClass*)malloc(sizeof(MyClass));
    pMyClass->MyClass();
    //
}
Error compiling pMyClass -> MyClass():
error C2273: 'function-style cast' : illegal as right side of '->'operator
It thinks MyClass is this type.
There are two solutions:
第一:pMyClass->MyClass::MyClass();
Second: new (pMyClass) MyClass();
The second usage involves the usage of C ++ placement new . Reference: http://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html shows what 
is the use of calling the constructor?
Sometimes, you may use malloc to allocate memory to class objects due to efficiency considerations, because malloc does not call the constructor, so this time will come in handy.
Also the following is possible, although built-in types don't have constructors.
int * i = ( int *) malloc( sizeof ( int ));
 new (i) int ();
 3. Why can't the copy (copy) constructor be passed by value
When you try to write the copy constructor as pass-by-value, you will find that the compilation fails, and the error message is as follows:
error: invalid constructor; you probably meant ' S (const S&) '
When there is a compilation error, you start to struggle. Why does the copy constructor have to be passed by reference? I searched for a lot of information on the Internet, and everyone basically means that if it is passed by value, it may cause an infinite loop. The compiler may not allow pass-by-value copy constructors for this reason, or it may be specified by the C ++ standard.
If the cause of the infinite loop is really, it should be like this:
class S
{
public:
S(int x):a(x){ }
S( const S st) // copy constructor 
{
a = st.a;
}
private:
int a;
};

intmain ()
{
S s1(2);
S s2(s1);
return 0;
}

When s2 is initialized, the copy constructor of s2 is called. Since it is passed by value, the system will re-apply for a space for the formal parameter st, and then call its own copy constructor to pass the value of the data member of s1 to st. When calling its own copy constructor, it is passed by value, so...
That is to say, as long as the copy constructor is called, a space will be re-applied, and as long as a space is re-applied, the copy constructor will be called, which will form an infinite loop. So copy constructors must not be pass-by-value.
 
4. The problem that the constructor/ destructor throws an exception
The constructor throws an exception:
    1. It is not recommended to throw an exception in the constructor;
     2. When the constructor throws an exception, the destructor will not be executed;
C ++ can only delete fully constructed objects (fully contructed objects), and an object cannot be fully constructed until its constructor has run completely. Each data member in an object should clean up itself, if the constructor throws an exception, the object's destructor will not run. If your object needs to undo some actions it has done (such as allocating memory, opening a file, or locking a semaphore), those actions that need to be undone must be remembered by a data member inside the object.
The destructor throws an exception:
    The destructor is called in two cases. The first is to delete an object under normal circumstances, such as when the object goes out of scope or is explicitly deleted. The second is that an object is deleted by the exception handling system during the stack - unwinding process of exception delivery.
In both cases above, the exception may or may not be active when the destructor is called. Unfortunately there is no way to distinguish between the two cases inside the destructor. So when writing destructors you must conservatively assume that an exception is activated, because if an exception is activated at the same time, the destructor also throws an exception and causes program control to transfer outside the destructor, C ++ The terminate function will be called. This function does exactly what its name says: it terminates your program, and it does so immediately, without even releasing the local objects.
Summarized as follows:
    1. The destructor should not throw exceptions;
     2. When there are some possible exceptions in the destructor, then the possible exceptions must be completely encapsulated inside the destructor, and must not be thrown. 3. When handling another exception, do not throw an exception from the destructor ;
    
    A good way to prevent resource leaks in constructors and destructors is to use smart points (smart pointers), C ++ STL provides the class template auto_ptr, use auto_ptr objects instead of raw pointers, you will no longer have heap objects that cannot be deleted And worry, even when an exception is thrown, the object can be deleted in time. Because auto_ptr's destructor uses the single-object form of delete, not delete[], auto_ptr cannot be used for pointers to arrays of objects. When copying an auto_ptr object or assigning its value to another auto_ptr object, the ownership of the base object is transferred from the original auto_ptr object to the copy, and the original auto_ptr object is reset to an unbound state. Therefore, auto_ptrs cannot be stored in standard library container types. If you want to use smart pointers as elements of STL containers, you can use shared_ptr from the Boost library.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325114542&siteId=291194637