C++开发基础——拷贝构造/移动构造/委托构造

一,对象的复制

对象复制时可以使用的运算符:=,(),{},={}等。

默认情况下的对象复制是将对象的每个成员变量逐个进行复制,可以通过定义拷贝构造函数或重载赋值运算符"operator="来改变默认操作。

代码样例:

对象的定义:

class Time
{
public:
    int Hour;
    int Minute;
    int Second;
};

对象的复制操作:

Time myTime;
Time myTime1 = myTime;
Time myTime2(myTime);
Time myTime3{myTime};
Time myTime4 ={myTime};

二,拷贝构造函数

1.概念介绍

    如果复制一个基本数据类型的变量,比如int,是可以直接进行拷贝的,如果复制一个类类型的变量,则只能使用拷贝构造函数类进行拷贝。

    第一个参数是类类型的引用。

    对象发生复制时会调用拷贝构造函数。

    如果定义一个类的时候没有定义自己的拷贝构造函数,编译器会根据需要生成一个默认的拷贝构造函数。

    只要发生值传递的方式产生一个新的对象,编译器就会调用拷贝构造函数进行初始化。

    由于拷贝构造函数被用来初始化非引用类型的对象,因此拷贝构造函数本身的参数必须是引用类型。

除了显式调用拷贝构造函数初始化对象,编译器遇到以下情况也会调用拷贝构造函数:

1.将一个对象作为实参传递给一个非引用类型的形参。

2.函数返回非引用类型的对象。

2.拷贝构造函数的代码样式

ClassName(const ClassName& param)
{
    //process code
}

3.代码样例

Demo1:

#include <iostream>
using namespace std;
class Time
{
public:
    int Hour;
    int Minute;
    int Second;
    Time() {
        std::cout << "调用了构造函数." << std::endl;
    }
    Time(const Time& tmpTime)
    {
        std::cout << "调用了拷贝构造函数." << std::endl;
    }
};
int main() {
    Time myTime;
    Time myTime1 = myTime;
    Time myTime2(myTime);
    Time myTime3{ myTime };
    Time myTime4 = { myTime };
    return 0;
}

运行结果:

调用了构造函数.
调用了拷贝构造函数.
调用了拷贝构造函数.
调用了拷贝构造函数.
调用了拷贝构造函数.

Demo2:

#include <iostream>
using namespace std;
class Wall {
private:
    double length;
    double height;
public:
    Wall(double len, double hgt) {
        length = len;
        height = hgt;
    }
    //copy constructor with a Wall object as parameter
    Wall(Wall& obj) {
        length = obj.length;
        height = obj.height;
    }
    double calculateArea() {
        return length * height;
    }
};
int main() {
    // create an object of Wall class
    Wall wall1(10.5, 8.6);
    // copy contents of wall1 to wall2
    Wall wall2 = wall1;
    cout << "Area of Wall 1: " << wall1.calculateArea() << endl;
    cout << "Area of Wall 2: " << wall2.calculateArea() << endl;
    return 0;
}

运行结果:

Area of Wall 1: 90.3
Area of Wall 2: 90.3

三,拷贝赋值运算符

1.概念介绍

    拷贝赋值运算符是二元运算符"operator="的重载,它只能被定义为类的成员函数,不能被定义为普通函数。

    拷贝赋值运算符把右操作数的成员数据拷贝给左操作数的成员。

    为了避免对象在拷贝过程中的不必要的复制,拷贝赋值运算符返回类型为引用类型。

    拷贝赋值运算符运行结束一般会返回指向该对象的this指针,方便被连续调用。

    拷贝赋值运算符的使用场景和拷贝构造函数不一样,如果对一个已经构造过的对象进行拷贝赋值,则此时并不会调用拷贝构造函数,而是调用拷贝赋值运算符。

    在重载赋值运算符的时候,也可以让拷贝赋值运算符复制不同类型的对象,只需要在重载的函数内部增加相应的类型转换逻辑即可实现。

    以下代码区分了拷贝构造和拷贝赋值:

Time myTime;
Time myTime1 = myTime; //调用了拷贝构造函数
Time myTime2;
myTime2 = myTime; //没有调用拷贝构造函数,调用了拷贝赋值运算符

2.拷贝赋值运算符的代码样式

ClassName& operator=(const ClassName& param)
{
    //process code
}

3.代码样例

#include <iostream>
using namespace std;

class Time
{
public:
    int Hour;
    int Minute;
    int Second;
    Time() {
        std::cout << "调用了构造函数." << std::endl;
    }
    Time(const Time& tmpTime)
    {
        std::cout << "调用了拷贝构造函数." << std::endl;
    }
    Time& operator=(const Time& tmpTime)
    {
        std::cout << "调用了拷贝赋值运算符." << std::endl;
        return *this;
    }
};

int main() {
    Time myTime;
    Time myTime1 = myTime;
    Time myTime2;
    myTime2 = myTime;
    return 0;
}

运行结果:

调用了构造函数.
调用了拷贝构造函数.
调用了构造函数.
调用了拷贝赋值运算符.

如果想禁止对象之间的拷贝赋值,可以将赋值运算符重载用private修饰,代码样例:

private:
    Time& operator=(const Time& tmpTime);

四,对象的移动

将对象A的所有权转移给对象B。

对于同一块内存,原先用来存放对象A,发生对象移动以后,这块内存用来存放对象B。

发生对象移动以后,原有的对象A将不能再被使用。

五,移动构造函数

1.概念介绍

    和拷贝赋值运算符一样,移动构造函数也是二元运算符"operator="的重载,它只能被定义为类的成员函数,不能被定义为普通函数。

    移动构造函数在构造对象的时候避免了拷贝一个新的对象。

    移动构造函数可以重复利用原有的内存空间,提供了代码效率。

    移动构造函数的形参是(&&)右值引用,而不是(&)左值引用。

    当一个对象发生移动以后,不会自主销毁,我们可以在移动构造函数的代码逻辑中显式地让该对象被析构。

2.移动构造函数的代码样式

ClassName(const ClassName&& param)
{
    //process code
}

六,移动赋值运算符算

1.概念介绍

和拷贝赋值运算符的逻辑类似,如果对一个已经构造过的对象进行移动,则此时并不会调用移动构造函数,而是调用移动赋值运算符。

以下场景的成员变量可以移动:

基本数据类型(int, float)的成员变量可以移动。

类类型的成员变量,且这个类有对应的移动操作相关的函数。

2.移动赋值运算符的代码样式

ClassName& operator=(const ClassName&& param)
{
    //process code
}

3.代码样例

Demo1:

#include <iostream>
using namespace std;

class Time
{
public:
    int Hour;
    int Minute;
    int Second;
    Time() {
        std::cout << "调用了构造函数." << std::endl;
    }
    Time(const Time& tmpTime)
    {
        std::cout << "调用了拷贝构造函数." << std::endl;
    }
    Time(const Time&& tmpTime)
    {
        std::cout << "调用了移动构造函数." << std::endl;
    }
    Time& operator=(const Time& tmpTime)
    {
        std::cout << "调用了拷贝赋值运算符." << std::endl;
        return *this;
    }
    Time& operator=(const Time&& tmpTime)
    {
        std::cout << "调用了移动赋值运算符." << std::endl;
        return *this;
    }
};
int main() {
    Time myTime;
    Time myTime1 = myTime;
    Time myTime2;
    myTime2 = myTime;
    Time myTime3 = std::move(myTime1);
    Time myTime4;
    myTime4 = std::move(myTime2);
    return 0;
}

运行结果:

调用了构造函数.
调用了拷贝构造函数.
调用了构造函数.
调用了拷贝赋值运算符.
调用了移动构造函数.
调用了构造函数.
调用了移动赋值运算符.

Demo2:

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

// Move Class
class Move {
private:
       int* data;
public:
       Move(int d)
       {
              data = new int;
              *data = d;
              cout << "Constructor is called for "
              << d << endl;
       };
       // Move Constructor
       Move(Move&& source)
              : data{ source.data }
       {
              cout << "Move Constructor for "
                      << *source.data << endl;
              source.data = nullptr;
       }
       // Destructor
       ~Move()
       {
              if (data != nullptr)
                      cout << "Destructor is called for "
                      << *data << endl;
              else
                      cout << "Destructor is called"
                      << " for nullptr "
                      << endl;
              delete data;
       }
};
int main()
{
       // Vector of Move Class
       vector<Move> vec;
       // Inserting Object of Move Class
       vec.push_back(Move{ 10 });
       vec.push_back(Move{ 20 });
       return 0;
}

运行结果:

Constructor is called for 10
Move Constructor for 10
Destructor is called for nullptr
Constructor is called for 20
Move Constructor for 20
Move Constructor for 10
Destructor is called for nullptr
Destructor is called for nullptr
Destructor is called for 10
Destructor is called for 20

七,委托构造函数

1.概念介绍

    类的构造函数可以在初始化列表的位置调用该类的另一个构造函数,这个构造函数就叫委托构造函数,因为它把构造对象的工作委托给了另一个构造函数。

    委托构造函数有助于精简函数代码。

    委托构造函数对其他构造函数的调用的相关代码,不能放在委托构造函数的函数体内,必须放在构造函数的初始化列表中。

    不能在委托构造函数的初始化列表中初始化成员变量,会导致代码编译失败。

    可以在委托构造函数的函数体中设置成员变量的值。

2.委托构造函数的代码样式

ClassName(param_list1, ...): ClassName(param_list2, ...)
{
    //process code
}

3.代码样例

Demo1:

#include <iostream>
using namespace std;

class Time
{
public:
    int Hour;
    int Minute;
    int Second;
    Time(int tmpHour) {
        Hour = tmpHour;
        std::cout << "调用了构造函数." << std::endl;
    }
    Time(int tmpHour2, int tmpMinute) : Time(tmpHour2)
    {
        Minute = tmpMinute;
        std::cout << "调用了委托构造函数." << std::endl;
    }
};
int main() {
    Time myTime(11, 30);
    return 0;
}

运行结果:

调用了构造函数.
调用了委托构造函数.

Demo2:

#include <iostream>
#include <string>
#include <string_view>

class Employee
{
private:
    std::string m_name{};
    int m_id{ 0 };
    void printCreated() const
    {
        std::cout << "Employee " << m_name << " created\n";
    }

public:
    Employee(std::string name)
        : m_name{ name }
    {
        std::cout << "In Constructor." << std::endl;
        printCreated();
    }
    Employee(std::string name, int id)
        : Employee(name)
    {
        std::cout << "In Delegating Constructor." << std::endl;
        m_id = id;
        printCreated();
    }
};

int main()
{
    Employee e1{ "James" };
    Employee e2{ "Dave", 42 };
}

运行结果:

In Constructor.
Employee James created
In Constructor.
Employee Dave created
In Delegating Constructor.
Employee Dave created

补充: 

构造函数名=default:让编译器生成默认的某构造函数。

构造函数名=delete:让编译器禁止调用某构造函数。

八,参考阅读

《C++新经典》

《C++ Primer》

《C++ Primer Plus》

C++ Constructors: Types and Copy Constructors (programiz.com)

Move Constructors in C++ with Examples - GeeksforGeeks

  • 30
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++中,每个类都有构造函数和析构函数。构造函数是一种特殊的成员函数,用于在创建对象时初始化对象的状态。析构函数是类的一个成员函数,用于在对象被销毁时释放资源。 C++中也有拷贝构造函数。拷贝构造函数是一种特殊的构造函数,用于创建一个新对象并将其初始化为另一个对象的副本。拷贝构造函数通常用于对象的值传递和返回值,以及对象的拷贝和赋值操作。 下面是一个示例代码,展示了如何定义和使用拷贝构造函数: ``` #include <iostream> using namespace std; class MyClass { private: int x; int y; public: // 构造函数 MyClass(int x, int y) { this->x = x; this->y = y; } // 拷贝构造函数 MyClass(const MyClass& obj) { this->x = obj.x; this->y = obj.y; } int getX() { return x; } int getY() { return y; } }; int main() { MyClass obj1(10, 20); MyClass obj2 = obj1; // 使用拷贝构造函数进行对象的拷贝 cout << obj1.getX() << endl; // 输出:10 cout << obj2.getX() << endl; // 输出:10 obj1.setX(30); cout << obj1.getX() << endl; // 输出:30 cout << obj2.getX() << endl; // 输出:10 return 0; } ``` 在上面的示例代码中,我们首先定义了一个 MyClass 类,包含了一个构造函数和一个拷贝构造函数。然后,我们创建了两个 MyClass 对象 obj1 和 obj2,其中 obj2 是通过 obj1 的拷贝构造函数创建的。最后,我们修改了 obj1 的 x 属性,发现 obj2 的 x 属性并未受到影响,说明 obj1 和 obj2 是独立的两个对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值