C++: methods are generated by compiler
Table of Contents
1 Methods are generated by compiler
A compiler generates four methods:
- Default constructor
- Copy constructor
- Operator =
- Destructor
If you want to implement your own version of either copy constructor or operator= or destructor then you must implement all other methods that are listed above.
#include <cstddef> #include <iostream> class IntArray { public: // Constructor explicit IntArray(size_t size) : size_(size), data_(new int[size]) { std::cout << "Constructor for " << this << std::endl; for (size_t i = 0; i != size_; ++i) this->data_[i] = 0; } // Copy constructor IntArray(IntArray const& a) : size_(a.size_), data_(new int[size_]) { std::cout << "Copy Constructor for " << this << std::endl; for (size_t i = 0; i != this->size_; ++i) this->data_[i] = a.data_[i]; } // Assign operator IntArray & operator=(IntArray const& a) { std::cout << "Operator =" << std::endl; if (this != &a) // Will create a temporary copy of a object and send it to swap IntArray(a).swap(*this); std::cout << "Return reference to this = " << this << std::endl; return *this; } // Destructor ~IntArray() { std::cout << "Destructor for " << this << std::endl; delete [] this->data_; } void swap(IntArray & a) { std::swap(this->size_, a.size_); std::swap(this->data_, a.data_); } private: size_t size_; int *data_; }; int main() { IntArray a1(10); IntArray a2(20); IntArray a3 = a1; a2 = a1; return 0; }
1.1 Constructor
Constructors are methods for structures initialization. If structure/class has no constructors, then default constructor (without arguments) will be automatically generated by compiler.
struct Point { Point () { // Default constructor x = y = 0; } Point (double x = 0, double y = 0) : x(x), y(y) {} private: double x; double y; }; int main() { Point p1; // {0, 0} Point p2(2); // {2, 0} Point p3(3, 7); }
Here is the constructor with initializers list that allows initialize fields before constructor was called:
Point (double x, double y) : y(0), x(y) {}
the key point here is that initialization order is the same as fields definition order
private: double x; double y;
x will go first and then y.
1.1.1 Explicit constructor
A constructor taking a single argument defines a conversion from its argument type.
struct Segment { Segment() = default; Segment (double length) : p2(length, 0) {} Point p1; Point p2; }; int main() { Segment s1; // (0, 0), (0, 0) Segment s2(10); // (0, 0), (10, 0) Segment s3 = 20; // (0, 0), (20, 0) = Segment(20) }
The way to avoid this conversion is to say that only explicit "conversion" is allowed
struct Segment { explicit Segment (double length) : p2(length, 0) {} … }; int main() { Point p = 20; // error }
1.1.2 Constructors and function definitons
If something in C++ looks like a function definition, it is a function definition!
class Point { public: explicit Point(double x=0, double y=0) : x(x), y(y) {} private: double x; double y; }; int main() { Point p1; // variable definition Point p2(); // function definition: // p2 takes no arguments, returns Point value double k = 5.1; Point p3(int(k)); // function definition Point p4((int)k); // variable definition }
1.2 Destructor
Destructor is a method that will be called when we delete a structure. The name of a destructor is the complement operator ~, followed by the name of the class; it is the complement of a constructor.
struct IntArray { explicit IntArray(size_t size) : size(size), data(new int[size]) {} ~IntArray() { delete [] data; } private: size_t size; int * data; };
So far, we can call the time between constructor calling and destructor calling is a variable's lifetime. Every time you call constructor, it locates the object into the stack. Destructors will be called in reversed order then.
1.3 Copy constructor
Here is the problem with a default copying of objects.
#include <cstddef> class IntArray { public: explicit IntArray(size_t size) : size_(size), data_(new int[size]) { for (size_t i = 0; i != size_; ++i) { this->data_[i] = 0; } } ~IntArray() { delete [] this->data_; } private: size_t size_; int *data_; }; int main() { IntArray a1(10); IntArray a2(20); IntArray a3 = a1; // memory leak! // a2->data_ isn't available anymore and won't be deleted with a destructor! a2 = a1; // will try to free the same memory area twice with // delete [] this->data_ // that will cause an error return 0; }
If you didn't implement a copy constructor, it will be generated automatically by compiler. Here we implement our own version of copy constructor:
struct IntArray { IntArray (IntArray const & a) : size_(a.size_), data_(new int[size_]) { for (size_t i = 0; i != size_; ++i) { data_[i] = a.data_[i]; } } private: size_t size_; int * data_; };
1.4 Assign operator
If you didn't implement a assign operator, it will be generated by a compiler as well. Here we implement an operator= method.
struct IntArray { IntArray & operator=(IntArray const & a) { if (this != &a) { delete [] data_; this->size_ = a.size_; this->data_ = new int[size_]; for (size_t i = 0; i != size_; ++i) { data_[i] = a.data_[i]; } } return *this; } private: size_t size_; int * data_; };
1.4.1 Disallow copying
If you want to disallow copying of object (it isn't clear how to copy database connection object, for example) you can declare a copy constructor and an assign operator as private methods and do not implement them.
class IntArray { IntArray (IntArray const & a); IntArray & operator= (IntArray const & a); size_t size_; int * data_; public: … };