拷贝与控制

本文介绍了C++中的拷贝构造函数,包括合成拷贝构造函数的原理和使用示例。接着讨论了拷贝初始化的两种形式以及在函数参数和返回值中的应用。此外,还讲解了重载赋值运算符和合成赋值运算符的作用。最后,阐述了析构函数的功能,以及何时会被调用,强调了在资源管理中的重要性。
摘要由CSDN通过智能技术生成

拷贝与控制

拷贝构造函数

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,

则此构造函数是拷贝构造函数

合成拷贝构造函数

当你没有显式定义拷贝构造函数时,编译器会自动创建一个默认的合成拷贝构造函数。合成拷贝构造函数用于创建一个对象,该对象与给定的对象具有相同的成员变量值。
合成拷贝构造函数使用了另一个同类型的对象作为参数,通过将该对象的成员变量值复制到新创建的对象中来完成拷贝。它通常用于对象的初始化、对象作为函数参数传递、对象作为函数返回值等场景。

#include <iostream>

class MyClass {
public:
    int value;

    // 合成拷贝构造函数

    // 其它成员函数和数据成员
};

int main() {
    MyClass obj1;
    obj1.value = 10;

    MyClass obj2(obj1);  // 使用合成拷贝构造函数创建obj2,并将obj1的值复制给obj2

    std::cout << obj2.value << std::endl;  // 输出 10

    return 0;
}

如果数组元素时类类型,则使用元素的拷贝构造函数来进行拷贝。

拷贝初始化

拷贝初始化的语法形式有两种:

  1. 使用等号(=)进行拷贝初始化:
ClassName obj1 = obj2;
  1. 拷贝初始化

将对象作为函数参数进行拷贝初始化:

void func(ClassName obj);

下面是一个示例,展示了拷贝初始化的用法:

#include <iostream>

class MyClass {
public:
    int value;

    MyClass(int val) : value(val) {}

    // 拷贝构造函数
    MyClass(const MyClass& other) : value(other.value) {}

    // 其它成员函数和数据成员
};

void func(MyClass obj) {
    std::cout << "Value inside func: " << obj.value << std::endl;
}

int main() {
    MyClass obj1(10);
    MyClass obj2 = obj1;  // 拷贝初始化

    std::cout << "Value of obj2: " << obj2.value << std::endl;

    func(obj1);  // 将对象作为函数参数进行拷贝初始化

    return 0;
}

参数和返回值

在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。当一个函数具有非引用的返回类型时,

返回值会被用来初始化调用方的结果。

拷贝初始化的限制

C++11中对拷贝初始化进行了限制,具体如下:

  1. 如果被拷贝的对象是一个右值,那么拷贝初始化是允许的,也就是说,可以把一个右值赋值给一个左值。

  2. 如果被拷贝的对象是一个左值,那么拷贝初始化只有在以下情况下才是允许的:

    a. 左值的类型和右值的类型相同。

    b. 左值的类型可以从右值的类型中隐式转换得到。

    c. 左值的类型具有移动构造函数,且被拷贝的对象是一个将要被销毁的临时对象。

    如果不满足上述任何一种情况,编译器将会报错。

    vector<int>v1(10);//正确 直接初始化	
    vector<int>v2=10;//错误 接受大小参数的构造函数是explicit的
    void f(vector<int>);// f的参数进行拷贝初始化
    f(10);//错误 不能用一个explicit的构造函数拷贝一个实参
    f(vector<int>(10));// 正确 从一个int 直接构造一个临时vector
    

拷贝赋值运算符

重载赋值运算符是指自定义一个类对象如何进行赋值操作。在C++中,赋值运算符通常写成类似于“operator=()”的形式。具体实现如下:

class MyClass {
public:
    MyClass& operator=(const MyClass& other) {
        // 检查自我赋值
        if (this == &other) {
            return *this;
        }
        // 进行赋值操作
        // ...
        return *this;
    }
};

在上面的代码中,重载了一个“operator=()”函数,它接受一个类型为MyClass的常引用参数other,在函数中,我们首先检查了自我赋值的情况(即,检查this指针是否等于&other),如果是则直接返回自身指针;否则进行赋值操作,并返回自身指针。

合成拷贝赋值运算符

如果一个类没有自定义赋值运算符,那么编译器会自动为其合成一个默认的赋值运算符。它执行的操作是逐个对数据成员进行赋值操作,例如:

class MyClass {
public:
    int x;
    int y;
};

MyClass a = {1, 2};
MyClass b = a; // 默认的赋值运算符将a的值逐个赋给b

在上面的代码中,b将会被赋值为{x=1, y=2},与a的值相同。

需要注意的是,C++合成的赋值运算符只是对数据成员进行逐个赋值操作,不会进行任何动态内存分配或资源管理操作。如果一个类需要进行动态内存分配或资源管理,那么就需要自定义赋值运算符来确保正确的释放和分配资源。

析构函数

析构函数是一种特殊的成员函数,用于在对象生命周期结束时释放对象所占用的资源。析构函数的名称与类名相同,前面加上一个波浪号(~)。

在C++中,析构函数的作用是释放对象所占用的内存,销毁对象的成员变量,关闭对象所打开的文件等。

析构函数的语法如下:

class MyClass {
public:
    // 构造函数
    MyClass();

    // 析构函数
    ~MyClass();
};

MyClass::MyClass() {
    // 构造函数的实现
}

MyClass::~MyClass() {
    // 析构函数的实现
}

在上面的代码中,我们定义了一个名为MyClass的类,它包括一个构造函数和一个析构函数。析构函数使用了波浪号(~)符号来标识,它没有参数和返回值。

需要注意的是,当一个对象被销毁时,析构函数会自动被调用。因此,我们可以在析构函数中释放对象所占用的资源,确保程序的正确性。同时,我们也应该避免在析构函数中抛出异常,因为这可能会导致程序崩溃。

在一个析构函数中,不存在类似构造函数中初始化列表的东西来控制成员如何销毁,析构

函数是隐式的。成员销毁时发生什么完全依赖于成语的类型。隐式销毁一个内置指针类型的

成员不会delete它所指向的对象。

什么时候会调用析构函数

无论何时一个对象被销毁,就会自动调用其析构函数

1、变量在离开其作用域时被销毁

2、当一个对象被销毁时,其成员被销毁

3、容器被销毁时,其元素被销毁

4、对于动态分配的对象,当对指向它的指针应用delete运算符被销毁

5、对于临时对象,当创建它的完整表达式结束时被销毁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值