Constructors in Cpp

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lewif/article/details/79972289

What is constructor?

A constructor is a member function of a class which initializes objects of a class. In C++,Constructor is automatically called when object(instance of class) create.It is special member function of the class.

How constructors are different from a normal member function?

A constructor is different from normal functions in following ways:
- Constructor has same name as the class itself
- Constructors don’t have return type
- A constructor is automatically called when an object is created.
- If we do not specify a constructor, C++ compiler generates a default constructor for us (expects no parameters and has an empty body).

Types of Constructors

  1. Default Constructors: Default constructor is the constructor which doesn’t take any argument. It has no parameters.
// Cpp program to illustrate the 
// concept of Constructors
#include <iostream>
using namespace std;

class construct
{ 
public: 
    int a, b;

        // Default Constructor
    construct()
    {
        a = 10;
        b = 20;
    }
};

int main()
{
        // Default constructor called automatically
        // when the object is created
    construct c;
    cout << "a: "<< c.a << endl << "b: "<< c.b;
    return 1;
}

Note: Even if we do not define any constructor explicitly, the compiler will automatically provide a default constructor implicitly. The default value of variables is 0 in case of automatic initialization.

2.Parameterized Constructors: It is possible to pass arguments to constructors. Typically, these arguments help initialize an object when it is created. To create a parameterized constructor, simply add parameters to it the way you would to any other function. When you define the constructor’s body, use the parameters to initialize the object.

// CPP program to illustrate 
// parameterized constructors
#include<iostream>
using namespace std;

class Point
{
    private:
        int x, y;
    public:
        // Parameterized Constructor
        Point(int x1, int y1) 
        { 
            x = x1; 
            y = y1; 
        }

        int getX()       
        { 
            return x; 
        }
        int getY()
        { 
            return y;
        }
    };

int main()
{
    // Constructor called
    Point p1(10, 15); 

    // Access values assigned by constructor
    cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();

    return 0;
}

When an object is declared in a parameterized constructor, the initial values have to be passed as arguments to the constructor function. The normal way of object declaration may not work. The constructors can be called explicitly or implicitly.

 Example e = Example(0, 50); // Explicit call

 Example e(0, 50);           // Implicit call

Constructor Overloading in C++

In C++, We can have more than one constructor in a class with same name, as long as each has a different list of arguments.This concept is known as Constructor Overloading and is quite similar to function overloading.

  • Overloaded constructors essentially have the same name (name of the class) and different number of arguments.
  • A constructor is called depending upon the number and type of arguments passed.
  • While creating the object, arguments must be passed to let compiler know, which constructor needs to be called.
// C++ program to illustrate 
// Constructor overloading
#include <iostream>
using namespace std;

class construct
{ 

public:
    float area; 

    // Constructor with no parameters
    construct()
    {
        area = 0;
    }

    // Constructor with two parameters
    construct(int a, int b)
    {
        area = a * b;
    }

    void disp()
    {
        cout<< area<< endl;
    }
};

int main()
{
    // Constructor Overloading 
    // with two different constructors
    // of class name
    construct o;
    construct o2( 10, 20);

    o.disp();
    o2.disp();
    return 1;
}

Copy Constructor in C++

A copy constructor is a member function which initializes an object using another object of the same class. A copy constructor has the following general function prototype:

   ClassName (const ClassName &old_obj); 

Following is a simple example of copy constructor.

include<iostream>
using namespace std;

class Point
{
private:
    int x, y;
public:
    Point(int x1, int y1) { x = x1; y = y1; }

    // Copy constructor
    Point(const Point &p2) {x = p2.x; y = p2.y; }

    int getX()            {  return x; }
    int getY()            {  return y; }
};

int main()
{
    Point p1(10, 15); // Normal constructor is called here
    Point p2 = p1; // Copy constructor is called here

    // Let us access values assigned by constructors
    cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();
    cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();

    return 0;
}

When is copy constructor called?

In C++, a Copy Constructor may be called in following cases:
1. When an object of the class is returned by value.
2. When an object of the class is passed (to a function) by value as an argument.
3. When an object is constructed based on another object of the same class.
4. When compiler generates a temporary object.

It is however, not guaranteed that a copy constructor will be called in all these cases, because the C++ Standard allows the compiler to optimize the copy away in certain cases, one example being the return value optimization (sometimes referred to as RVO).

When is user defined copy constructor needed?
If we don’t define our own copy constructor, the C++ compiler creates a default copy constructor for each class which does a member wise copy between objects. The compiler created copy constructor works fine in general. We need to define our own copy constructor only if an object has pointers or any run time allocation of resource like file handle, a network connection..etc.

Default constructor does only shallow copy.
image

Deep copy is possible only with user defined copy constructor. In user defined copy constructor, we make sure that pointers (or references) of copied object point to new memory locations.
image

Copy constructor vs Assignment Operator

Which of the following two statements call copy constructor and which one calls assignment operator?

MyClass t1, t2;
MyClass t3 = t1;  // ----> (1)
t2 = t1;          // -----> (2) 

Copy constructor is called when a new object is created from an existing object, as a copy of the existing object. Assignment operator is called when an already initialized object is assigned a new value from another existing object. In the above example (1) calls copy constrictor and (2) calls assignment operator. See this for more details.

Write an example class where copy constructor is needed?

Following is a complete C++ program to demonstrate use of Copy constructor. In the following String class, we must write copy constructor.

#include<iostream>
#include<cstring>
using namespace std;

class String
{
private:
    char *s;
    int size;
public:
    String(const char *str = NULL); // constructor
    ~String() { delete [] s;  }// destructor
    String(const String&); // copy constructor
    void print() { cout << s << endl; } // Function to print string
    void change(const char *);  // Function to change
};

String::String(const char *str)
{
    size = strlen(str);
    s = new char[size+1];
    strcpy(s, str);
}

void String::change(const char *str)
{
    delete [] s;
    size = strlen(str);
    s = new char[size+1];
    strcpy(s, str);
}

String::String(const String& old_str)
{
    size = old_str.size;
    s = new char[size+1];
    strcpy(s, old_str.s);
}

int main()
{
    String str1("GeeksQuiz");
    String str2 = str1;

    str1.print(); // what is printed ?
    str2.print();

    str2.change("GeeksforGeeks");

    str1.print(); // what is printed now ?
    str2.print();
    return 0;
}

Output:

GeeksQuiz
GeeksQuiz
GeeksQuiz
GeeksforGeeks

What would be the problem if we remove copy constructor from above code?

If we remove copy constructor from above program, we don’t get the expected output. The changes made to str2 reflect in str1 as well which is never expected.

#include<iostream>
#include<cstring>
using namespace std;

class String
{
private:
    char *s;
    int size;
public:
    String(const char *str = NULL); // constructor
    ~String() { delete [] s;  }// destructor
    void print() { cout << s << endl; }
    void change(const char *);  // Function to change
};

String::String(const char *str)
{
    size = strlen(str);
    s = new char[size+1];
    strcpy(s, str);
}

void String::change(const char *str)
{
    delete [] s;
    size = strlen(str);
    s = new char[size+1];
    strcpy(s, str);
}

int main()
{
    String str1("GeeksQuiz");
    String str2 = str1;

    str1.print(); // what is printed ?
    str2.print();

    str2.change("GeeksforGeeks");

    str1.print(); // what is printed now ?
    str2.print();
    return 0;
}

Output:

GeeksQuiz
GeeksQuiz
GeeksforGeeks
GeeksforGeeks

Can we make copy constructor private?

Yes, a copy constructor can be made private. When we make a copy constructor private in a class, objects of that class become non-copyable. This is particularly useful when our class has pointers or dynamically allocated resources. In such situations, we can either write our own copy constructor like above String example, or make a private copy constructor so that users get compiler errors rather than surprises at run time.

Why argument to a copy constructor must be passed as a reference?

A copy constructor is called when an object is passed by value. Copy constructor itself is a function. So if we pass argument by value in a copy constructor, a call to copy constructor would be made to call copy constructor which becomes a non-terminating chain of calls. Therefore compiler doesn’t allow parameters to be pass by value.

Why argument to a copy constructor should be const?

When we create our own copy constructor, we pass an object by reference and we generally pass it as a const reference.
One reason for passing const reference is, we should use const in C++ wherever possible so that objects are not accidentally modified. This is one good reason for passing reference as const, but there is more to it. For example, predict the output of following C++ program. Assume that copy elision is not done by compiler.

#include<iostream>
using namespace std;

class Test
{
   /* Class data members */
public:
   Test(Test &t) { /* Copy data members from t*/}
   Test()        { /* Initialize data members */ }
};

Test fun()
{
    cout << "fun() Called\n";
    Test t;
    return t;
}

int main()
{
    Test t1;
    Test t2 = fun();
    return 0;
}

Output:

Compiler Error in line "Test t2 = fun();"

The program looks fine at first look, but it has compiler error. If we add const in copy constructor, the program works fine, i.e., we change copy constructor to following.

Test(const Test &t) { cout << "Copy Constructor Called\n"; }

Or if we change the line “Test t2 = fun();” to following two lines, then also the program works fine.

Test t2; 
t2 = fun();

The function fun() returns by value. So the compiler creates a temporary object which is copied to t2 using copy constructor in the original program (The temporary object is passed as an argument to copy constructor). The reason for compiler error is, compiler created temporary objects cannot be bound to non-const references and the original program tries to do that. It doesn’t make sense to modify compiler created temporary objects as they can die any moment.


每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如:

A(void); // 缺省的无参数构造函数

A(const A &a); // 缺省的拷贝构造函数

~A(void); // 缺省的析构函数

A & operate =(const A &a); // 缺省的赋值函数

拷贝构造函数和赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。以下程序中,第三个语句和第四个语句很相似,你分得清楚哪个调用了拷贝构造函数,哪个调用了赋值函数吗?

String a(“hello”);

String b(“world”);

String c = a; // 调用了拷贝构造函数,最好写成c(a);

c = b; // 调用了赋值函数
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页