类模板以及赋值运算符重载、拷贝构造函数

本文探讨了在C++中如何实现深拷贝,并通过模板化的方法支持不同类型的对象之间的拷贝构造和赋值操作。文章详细介绍了自定义拷贝构造函数和赋值运算符重载的方法,以解决默认浅拷贝带来的问题。

编译器默认的拷贝构造函数,是发生的浅拷贝,像指针的赋值就会让指针指向同一个地址空间,析构时就会对同一个地址空间释放两次,就会造成程序崩溃.
自定义在模板内的拷贝构造函数:

Queue(const Queue<T> &src)//类对象的拷贝构造函数
    {
        //Queue();
        _pfirst = _prear = NULL;
        QueueItem*p =src._pfirst;
        while (p != NULL)
        {
            addQue(p->_data);
            p = p->_pnext;
        }
    }

上述拷贝构造函数仅仅支持同种类型的对象之间的拷贝构造,对于不同类型的对象之间的拷贝构造就需要建立如下模板(但是必须在定义友元类):

private中需要先定义如下:
template<typename E>
friend class Queue;
template<typename E>
    Queue(const Queue<E>&src)//自定义拷贝构造函数的模板,Queue<E>为新类型,必须先声明Queue<E>类类型为此类型的友元类型,此模板不能替代上述同类型的拷贝构造
    {
        cout << "const Queue<E>& 模板" << endl;
        _pfirst = _prear = NULL;
        Queue<E>::QueueItem*p = src._pfirst;//必须加Queue<E>这个作用域,否则指针的类型不一样,会出现错误。
        while (p != NULL)
        {
            addQue(p->_data);
            p = p->_pnext;
        }
    }

此模板在模板外定义:

//不同类型对象的拷贝构造函数的模板外定义
template<typename T>
template<typename E>
Queue<T>::Queue(const Queue<E> &src)
{
    Queue<E>::QueueItem*p = src._pfirst;
    while (p != NULL)
    {
        addQue(p->_data);
        p = p->_pnext;
    }
}

此支持不同类型对象之间的拷贝构造函数不能替代同种类型对象的的拷贝构造函数。
赋值运算符同样需要自定义,否则就会出现和拷贝构造相同的问题,自定义的相同类型的对象之间的赋值运算符重载函数:

void operator=(const Queue<T> &src)//自定义的赋值运算符的重载函数,避免了编译器默认的浅拷贝,造成的内存泄漏等问题
    {
        cout << "operator Queue<T> &src" << endl;
        if (this == &src)
        {
            return;
        }
        if (_pfirst != NULL)
        {
            while (_pfirst != _prear)
            {
                QueueItem*tmp = _pfirst->_pnext;
                delete _pfirst;
                _pfirst = tmp;
            }
        }
        delete _pfirst;
        _pfirst = _prear = NULL;//必须将头尾指针指向NULL,否则就会从_pfirst的位置向后添加元素,而_pfirst的位置确是已经释放的随机值

        QueueItem*p = src._pfirst;
        while (p != NULL)
        {
            addQue(p->_data);
            p = p->_pnext;
        }
    }

不同类型对象之间的赋值运算符的重载函数模板:

template<typename E>
    void operator=(const Queue<E> &src)//不同类型对象之间的赋值运算
    {
        cout << "operator Queue<T> &src模板" << endl;

        if (_pfirst != NULL)
        {
            while (_pfirst != _prear)
            {
                QueueItem*tmp = _pfirst->_pnext;
                delete _pfirst;
                _pfirst = tmp;
            }
        }
        delete _pfirst;
        _pfirst = _prear = NULL;

        Queue<E>::QueueItem*p = src._pfirst;
        while (p != NULL)
        {
            addQue(p->_data);
            p = p->_pnext;
        }
    }

在没有给定此模板时,此句int_queue = double_queue会先调用不同类型对象直接的拷贝构造函数先生成同种类型得临时对象,再调用同种类型的赋值运算符的重载函数赋值成功,当定义了此 模板就直接实例化此模板一次就完成赋值。

主函数:


int main()
{

    Queue<int> int_queue;
    int_queue.addQue(10);
    int_queue.addQue(20);

    Queue<double> double_queue = Queue<double>();
    double_queue.addQue(30.5);

    Queue<int> int1_queue(int_queue);//相同类型对象之间的拷贝构造,需要自定义拷贝构造函数,否则编译器默认的造成浅拷贝(指针的赋值)导致出错
    int1_queue.addQue(40);

    Queue<int> int2_queue(double_queue);//不同类型对象之间的拷贝构造,编译器不会产生,需要自定义。
    cout <<"int_queue------------------------------------------" << endl;
    int_queue.show();
    cout << "int1_queue----------------------------------------" << endl;
    int1_queue.show();
    cout << "double_queue--------------------------------------" << endl;
    double_queue.show();
    cout << "int2_queue----------------------------------------" << endl;
    int2_queue.show();
    cout << "赋值运算符的重载函数---------------------------------------------" << endl;
    //int_queue = int1_queue;
    //int_queue = Queue<int>(int1_queue);

    //int_queue = double_queue;在没有给定不同类型的对象之间的赋值运算符的重载函数时,编译器会先调用不同类型对象的拷贝构造函数
    //将double类型的对象构造成Int类型的临时对象,在调用相同类型对象之间的赋值运算符赋值给int_queue
    int_queue = Queue<double>(double_queue);
    int_queue.show();
    return 0;
}

运行结果:
Visual Leak Detector Version 2.4RC2 installed.
const Queue& 模板
int_queue——————————————
10 20
int1_queue—————————————-
10 20 40
double_queue————————————–
30.5
int2_queue—————————————-
30
赋值运算符的重载函数———————————————
operator Queue &src模板
30
No memory leaks detected.
Visual Leak Detector is now exiting.
请按任意键继续…

### `std::map` 的赋值运算符重载拷贝构造函数的关系 在 C++ 中,`std::map` 是标准模板库(STL)提供的关联容器,其赋值运算符重载拷贝构造函数都负责实现深拷贝语义。具体而言,`std::map` 的赋值运算符重载在执行赋值操作时,确实会调用元素类型的拷贝构造函数来完成深拷贝,以确保新容器与原容器拥有各自独立的元素副本。 例如,当执行以下代码时: ```cpp std::map<int, std::string> original = {{1, "one"}, {2, "two"}}; std::map<int, std::string> copy; copy = original; // 调用赋值运算符重载 ``` 在此过程中,`map` 容器的赋值运算符会逐个调用键值对类型的拷贝构造函数(即 `int` 和 `std::string` 的拷贝构造函数),从而确保 `copy` 与 `original` 中的元素是彼此独立的副本。这种行为符合 STL 容器的一般设计原则,即所有容器操作(包括拷贝和赋值)都执行深拷贝[^1]。 ### 拷贝构造函数赋值运算符的调用时机 虽然 `std::map` 的赋值运算符重载拷贝构造函数都执行深拷贝,但它们的调用时机不同: - **拷贝构造函数** 在初始化新对象时被调用,例如 `std::map<int, std::string> copy(original);`。 - **赋值运算符重载** 在已有对象被重新赋值时调用,例如 `copy = original;`。 在内部实现上,`map` 的赋值运算符重载通常会先清空目标容器的现有内容,然后逐个复制源容器的元素,每个元素的拷贝构造函数都会被调用。这种机制确保了赋值操作后,两个容器的元素互不影响。 ### 深拷贝的实现细节 `std::map` 的深拷贝特性不仅适用于基本类型(如 `int`、`double`),也适用于自定义类型。只要键类型和值类型都支持拷贝构造函数赋值运算符,`map` 就能正确地完成深拷贝。例如,当键或值为自定义结构体时,必须确保该结构体提供了有效的拷贝构造函数,否则编译器将无法生成默认的拷贝行为。 以下是一个自定义结构体作为 `map` 键类型的示例: ```cpp struct Point { int x, y; Point(int x, int y) : x(x), y(y) {} Point(const Point& other) : x(other.x), y(other.y) {} // 拷贝构造函数 }; std::map<Point, std::string> mp1; mp1[{1, 2}] = "Point A"; std::map<Point, std::string> mp2 = mp1; // 调用拷贝构造函数 ``` 在此示例中,`map` 的拷贝构造函数会调用 `Point` 类型的拷贝构造函数,确保每个键值对都被正确复制。 ### 总结 `std::map` 的赋值运算符重载在执行时会调用元素类型的拷贝构造函数,以实现深拷贝语义。这种机制确保了容器之间的独立性,避免了共享底层数据可能引发的副作用。只要元素类型支持拷贝构造,`map` 的赋值操作就能安全、高效地完成深拷贝。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值