UP | HOME

C++: methods are generated by compiler

Table of Contents

1 Methods are generated by compiler

A compiler generates four methods:

  1. Default constructor
  2. Copy constructor
  3. Operator =
  4. 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:
    …
};

Author: Pavel Vavilin

Created: 2018-03-05 Mon 12:47

Validate