[C + + deep analysis] 12. Constructor and copy constructor

Article directory

1 problem

What are the initial values of member variables i and j in the following class definition?

  • When creating objects on the stack or heap, the member variables are initially random values
  • When creating an object in a static store, the member variable is initially 0

Global variables are in the static storage area, local variables are in the stack, malloc requests are in the heap, new requests space from the free storage area, and many free storage areas are implemented by the heap.

2 constructor

The function of a constructor is to construct an object with a specific value when the object is created, or to initialize the object to a specific state.

  • Constructor function is the same as class name, no return type
  • Constructor is automatically called when the object is defined

Programming experiment: a preliminary study of constructors

// 12-1.cpp
#include<stdio.h>
class Test
{
private:
    int i;
    int j;
public:
    Test(int newi, int newj)
    {
        printf("Test() Begin\n");
        i = newi;
        j = newj;
    }
    int getI() {return i;}
    int getJ() {return j;}
};
int main()
{
    Test t1(1, 2);
    printf("t1.i = %d\n", t1.getI());
    printf("t1.j = %d\n", t1.getJ());
    return 0;
}

2.1 constructor overload

  • Constructors can define parameters as needed
  • There can be multiple overloaded constructors in a class, following the rules of C + + overloads

Note: object definitions and declarations are different

  • Object definition: request the space of the object and call the constructor
  • Object declaration: tells the compiler that such an object exists


Programming experiment: constructor overload

// 12-2.cpp
#include<stdio.h>
class Test
{
public:
    Test()
    {
        printf("Test()\n");
    }
    Test(int v)
    {
        printf("Test(int v), v = %d\n", v);
    }
};
int main()
{
    Test t;					// Call Test()
    Test t1(1);				// Call Test(int v)
    Test t2 = 2;			// Call Test(int v)
    int i(100);				// Initialization
    printf("i = %d\n", i);
    return 0;
}

2.2 call the constructor manually

In general, constructors are called automatically when an object is defined, and in some special cases, constructors need to be called manually.

Programming experiment: calling constructor manually

// 12-3.cpp
#include<stdio.h>
class Test
{
private:
    int m_value;
public:
    Test()
    {
        m_value = 0;
        printf("Test()\n");
    }
    Test(int v)
    {
        m_value = v;
        printf("Test(v), v = %d\n", v);
    }
    int getvalue()
    {
        return m_value;
    }
};
int main()
{
    Test ta[3] = {Test(), Test(1), Test(2)};
    for (int i = 0; i < 3; i++)
    {
        printf("ta[%d].getvalue() = %d\n", i, ta[i].getvalue());
    }
    Test t = Test(100);
    printf("t.getvalue() = %d\n", t.getvalue());
    return 0;
}

Class has two constructors, one with parameters and one without parameters. The main() function defines an array ta[3]. We specify that the first element of the array calls the constructor without parameters, and the last two call the constructor with parameters.

Test t = Test(100); also a way to specify a constructor.

Compile run:

$ g++ 12-3.cpp -o 12-3
$ ./12-3
Test()
Test(v), v = 1
Test(v), v = 2
ta[0].getvalue() = 0
ta[1].getvalue() = 1
ta[2].getvalue() = 2
Test(v), v = 100
t.getvalue() = 100

2.3 develop array class to solve the security problem of native array

Requirement: develop an array class to solve the security problem of native array

  • Provide function to get array length
  • Provide function to get array elements
  • Provide function settings array elements

Looking directly at the code, IntArray.h is used to define classes, and IntArray.cpp is used to implement class member functions.

// IntArray.h
#ifndef _INTARRAY_H_
#define _INRARRAY_H_
class IntArray
{
private:
    int m_length;
    int* m_pointer;
public:
    IntArray(int len);
    int length();
    bool get(int index, int& value);
    bool set(int index, int value);
    void free();
};
#endif
#include"IntArray.h"
IntArray::IntArray(int len)
{
    m_pointer = new int[len];
    for (int i = 0; i < len; i++)
    {
        m_pointer[i] = 0;
    }
    m_length = len;
}
int IntArray::length()
{
    return m_length;
}
bool IntArray::get(int index, int& value)
{
    bool ret = (index >= 0 && index < m_length);
    if (ret)
    {
        value = m_pointer[index];
    }
    return ret;
}
bool IntArray::set(int index, int value)
{
    bool ret = (index >= 0 && index < m_length);
    if (ret)
    {
        m_pointer[index] = value;
    }
    return ret;
}
void IntArray::free()
{
    delete[]m_pointer;
}
// 12-4.cpp
#include<stdio.h>
#include"IntArray.h"
int main()
{
    IntArray a(5);
    for (int i = 0; i < a.length(); i++)
    {
        a.set(i, i+1);
    }
    for (int i = 0; i < a.length(); i++)
    {
        int value = 0;
        if (a.get(i, value))
        {
            printf("a[%d] = %d\n", i, value);
        }
    }
    a.free();
    return 0;
}
$ g++ 12-4.cpp IntArray.cpp -o 12-4
$ ./12-4
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5

3 copy constructor

Two special constructors

  • non-parameter constructor
    • When there is no constructor defined in the class, the compiler provides a parameterless constructor by default, and the function body is empty
  • copy constructor
    • When there is no copy constructor defined in the class, the compiler provides a copy constructor by default, which simply copies the value of the member variable
// 12-5.cpp
#include<stdio.h>
class Point
{
public:
	Point(int xx = 0, int yy = 0)
	{
		x = xx;
		y = yy;
	}
	Point(const Point& p)
	{
		x = p.x;
		y = p.y;
		printf("Calling the copy constructor\n");
	}
	int getX(){ return x; }
	int getY(){ return y; }
private:
	int x;
	int y;
};
//Functions whose parameters are Point class objects
void fun1(Point p) {
	printf("p.getX() = %d\n", p.getX());
}
//Function with return value of Point class object
Point fun2() {
	Point a(1, 2);
	return a;
}
int main() {
	Point a(4, 5);
	Point b = a; 			//In case 1, initialize B with A. First call to copy constructor
	printf("b.getX() = %d\n", b.getX());
	fun1(b); 				//In case 2, object B is the actual parameter of fun1. Second call to copy constructor
	b = fun2(); 			//In case 3, the return value of the function is a class object, and the copy constructor is called when the function returns
	printf("b.getX() = %d\n", b.getX());
	return 0;
}
Calling the copy constructor
b.getX() = 4
Calling the copy constructor
p.getX() = 4
Calling the copy constructor
b.getX() = 1

3.1 deep copy and shallow copy

For simple classes, the default copy constructor is generally sufficient. But when a class holds other resources, such as dynamically allocated memory, pointers to other data, the default copy constructor cannot copy these resources. We must explicitly define the copy constructor to copy all the data of the object.

For example, a pointer variable in the class Point applies for space in the constructor and frees up space in the destructor. If you use shallow copy, the pointers in p1 and p2 Point to the same address space. During the destructor, the memory of delete will be faulted twice.

When deep copy is used, a block of memory of the same size is reallocated, and the data is copied down, so that p1 and p2 point to their own data block respectively, and their memory is released during the structure analysis.

  • The copy constructor provided by the compiler only makes shallow copies

// 12-5.cpp
#include<stdio.h>
class Point
{
public:
    Point(int v, int xx = 0, int yy = 0)
    {
        p = new int;
        *p = v;
        x = xx;
        y = yy;
    }
    Point(const Point& pp)
    {
    	p = new int;
    	*p = *pp.p;
        x = pp.x;
        y = pp.y;
    }
    int getX(){ return x; }
    int getY(){ return y; }
    int* getP(){ return p; }
    ~Point(){ delete p; }
private:
    int* p;
    int x;
    int y;    
};
int main() {
    Point p1(3);
    Point p2(p1);
    printf("p1.x = %d, p1.y = %d, p1 = %p\n", p1.getX(), p1.getY(), p1.getP());
    printf("p2.x = %d, p2.y = %d, p2 = %p\n", p2.getX(), p2.getY(), p2.getP());
	return 0;
}
$ g++ 12-6.cpp -o 12-6
$ ./12-6
p1.x = 0, p1.y = 0, p1 = 0x5653bc52fe70
p2.x = 0, p2.y = 0, p2 = 0x5653bc52fe90

From the result of running, we can see that the two pointers point to different memory.

3.2 when do I need a deep copy?

When a member of an object uses resources in the system

  • Member specifies dynamic memory space
  • Member opened a file in external storage
  • Members use the network interface in the system

General principle: user defined copy constructor, must realize deep copy!!!

3.3 improvement of array class

Because of the dynamic space application in the array class implemented previously, the copy constructor is added here to realize the deep copy.

// IntArray.h
#ifndef _INTARRAY_H_
#define _INRARRAY_H_
class IntArray
{
private:
    int m_length;
    int* m_pointer;
public:
    IntArray(int len);
    IntArray(const IntArray& obj);
    int length();
    bool get(int index, int& value);
    bool set(int index, int value);
    ~IntArray();
};
#endif
#include"IntArray.h"
IntArray::IntArray(int len)
{
    m_pointer = new int[len];
    for (int i = 0; i < len; i++)
    {
        m_pointer[i] = 0;
    }
    m_length = len;
}
IntArray::IntArray(const IntArray& obj)
{
    m_length = obj.m_length;
    m_pointer = new int[obj.m_length];
    for (int i = 0; i < obj.m_length; i++)
    {
        m_pointer[i] = obj.m_pointer[i];
    }
}

int IntArray::length()
{
    return m_length;
}
bool IntArray::get(int index, int& value)
{
    bool ret = (index >= 0 && index < m_length);
    if (ret)
    {
        value = m_pointer[index];
    }
    return ret;
}
bool IntArray::set(int index, int value)
{
    bool ret = (index >= 0 && index < m_length);
    if (ret)
    {
        m_pointer[index] = value;
    }
    return ret;
}
IntArray::~IntArray()
{
    delete[]m_pointer;
}
// 12-6.cpp
#include<stdio.h>
#include"IntArray.h"
int main()
{
    IntArray a(5);
    for (int i = 0; i < a.length(); i++)
    {
        a.set(i, i+1);
    }
    for (int i = 0; i < a.length(); i++)
    {
        int value = 0;
        if (a.get(i, value))
        {
            printf("a[%d] = %d\n", i, value);
        }
    }
    IntArray b = a;
    for(int i=0; i<b.length(); i++)
    {
        int value = 0;
        if( b.get(i, value) )
        {
            printf("b[%d] = %d\n", i, value);
        }
    }
    return 0;
}

4 Summary

1. Constructor can overload
2. Object definition triggers a call to the constructor
3. Constructor can be called manually
4. Shallow copy and deep copy of copy constructor

248 original articles published, praised by 115, visited 90000+
Private letter follow

Tags: Programming network

Posted on Tue, 28 Jan 2020 23:25:02 -0800 by Noctagon