类用途(复制和交换)和普通实现的复制赋值运算符给出不同的结果

Copy-Assignment Operator of class uses (copy and swap) and ordinary implement give different result

提问人:David Zhou 提问时间:1/10/2022 更新时间:1/10/2022 访问量:126

问:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <memory>

using std::cout;
using std::vector;


class Has_ptr {
public:
    friend void swap(Has_ptr &lhs, Has_ptr &rhs);
    friend bool operator<(const Has_ptr &a, const Has_ptr &b);

    
    // constructor
    Has_ptr(const std::string &s, int num) : ptr(new std::string(s)), i(num) {}

    // copy constructor
    Has_ptr(const Has_ptr &a) : ptr(new std::string(*a.ptr)), i(a.i) {}


    // copy-assign operator
    /*
    Has_ptr& operator=(Has_ptr tmp) 
    {
        this->swap(tmp);
        return *this;
    }
    */

    /*
    Has_ptr& operator=(Has_ptr &tmp) 
    {
        std::string *x = new std::string(*(tmp.ptr));
        delete ptr;
        ptr = x;
        i = tmp.i;
        return *this;
    }
    */

    

    // destructor
    ~Has_ptr() {
        delete ptr;
    }
    
    void swap(Has_ptr &rhs) {
        using std::swap;
        swap(ptr, rhs.ptr);
        swap(i, rhs.i);
    }
    
    void print() {
        cout << *ptr << i;
    }
private:
    std::string *ptr;
    int i;
};


// add a swap function
void swap(Has_ptr &lhs, Has_ptr &rhs) {
    lhs.swap(rhs);
}


bool operator<(const Has_ptr &a, const Has_ptr &b) {
    return a.i < b.i;
}

在 C++ Primer 5th,13.3 Swap 中,练习 13.31。问题需要给类Has_ptr一个<运算符,并创建一个元素很少的向量,然后对向量进行排序。类的实现如上所述。

具有 2 个数据成员的Has_ptr

  1. 字符串指针 PTR,指向动态内存
  2. 国际 i

Has_ptR 类具有以下复制控制成员

  1. 构造函数接受一个字符串和一个整数。
  2. copy_constructor,实现为类的行为类似于值。
  3. 析构函数,用于删除 PTR 分配的动态内存。

我尝试了复制分配运算符的两个实现。 首先,我创建临时字符串指针变量 x 来保存 RHS 指针,然后删除 LHS 指针,最后将 RHS 分配给 LHS。但是,此实现使代码无法编译。

Has_ptr& operator=(Has_ptr &tmp) 
{
    std::string *x = new std::string(*(tmp.ptr));
    delete ptr;
    ptr = x;
    i = tmp.i;
    return *this;
}

第二个实现使用了复制和交换。这使得代码编译器和运行是碎片化的。

// copy and swap
Has_ptr& operator=(Has_ptr tmp) 
{
    this->swap(tmp);
    return *this;
}

我的主要功能如下

int main() {
    Has_ptr a("a", 0), b("b", 1), c("c", 2);
    a.print();
    vector<Has_ptr> coll{a, b, c};

    std::sort(coll.begin(), coll.end());

    for (auto h : coll) 
        h.print();
}

第二个实现在编译器时不生成错误/警告消息,并生成以下输出

a0a0

第一个实现产生以下错误消息

In file included from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\algorithm:62:0,             

    from 13_31.c++:9:
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h: In instantiation of 'void std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]':
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1882:25:   required from 'void std::__final_insertion_sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1968:31:   required from 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:4707:18:   required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >]'
13_31.c++:88:39:   required from here
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1849:17: error: invalid initialization of non-const reference of type 'Has_ptr&' from an rvalue of type 'std::remove_reference<Has_ptr&>::type {aka Has_ptr}'
        *__first = _GLIBCXX_MOVE(__val);
                 ^
13_31.c++:42:14: note:   initializing argument 1 of 'Has_ptr& Has_ptr::operator=(Has_ptr&)'
     Has_ptr& operator=(Has_ptr &tmp)
              ^~~~~~~~
In file included from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:61:0,
                 from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\algorithm:62,
                 from 13_31.c++:9:
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_heap.h: In instantiation of 'void std::__pop_heap(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]':
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1672:19:   required from 'void std::__heap_select(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1930:25:   required from 'void std::__partial_sort(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1945:27:   required from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Size = int; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1965:25:   required from 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:4707:18:   required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >]'
13_31.c++:88:39:   required from here
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_heap.h:246:17: error: invalid initialization of non-const reference of type 'Has_ptr&' from an rvalue of type 'std::remove_reference<Has_ptr&>::type {aka Has_ptr}'
       *__result = _GLIBCXX_MOVE(*__first);
                 ^
13_31.c++:42:14: note:   initializing argument 1 of 'Has_ptr& Has_ptr::operator=(Has_ptr&)'
     Has_ptr& operator=(Has_ptr &tmp)
              ^~~~~~~~
In file included from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:61:0,
                 from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\algorithm:62,
                 from 13_31.c++:9:
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_heap.h: In instantiation of 'void std::__adjust_heap(_RandomAccessIterator, _Distance, _Distance, _Tp, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Distance = int; _Tp = Has_ptr; _Compare = __gnu_cxx::__ops::_Iter_less_iter]':
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_heap.h:335:22:   required from 'void std::__make_heap(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1669:23:   required from 'void std::__heap_select(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1930:25:   required from 'void std::__partial_sort(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1945:27:   required from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Size = int; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1965:25:   required from 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:4707:18:   required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >]'
13_31.c++:88:39:   required from here
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_heap.h:220:29: error: invalid initialization of non-const reference of type 'Has_ptr&' from an rvalue of type 'std::remove_reference<Has_ptr&>::type {aka Has_ptr}'  
    *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __secondChild));
                             ^
13_31.c++:42:14: note:   initializing argument 1 of 'Has_ptr& Has_ptr::operator=(Has_ptr&)'
     Has_ptr& operator=(Has_ptr &tmp)
              ^~~~~~~~
In file included from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:61:0,
                 from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\algorithm:62,
                 from 13_31.c++:9:
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_heap.h:226:29: error: invalid initialization of non-const reference of type 'Has_ptr&' from an rvalue of type 'std::remove_reference<Has_ptr&>::type {aka Has_ptr}'
    *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first
                             ^
13_31.c++:42:14: note:   initializing argument 1 of 'Has_ptr& Has_ptr::operator=(Has_ptr&)'
     Has_ptr& operator=(Has_ptr &tmp)
              ^~~~~~~~
In file included from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\char_traits.h:39:0,
                 from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\ios:40,
                 from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\ostream:38,
                 from c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\iostream:39,
                 from 13_31.c++:6:
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algobase.h: In instantiation of 'static _BI2 std::__copy_move_backward<true, false, std::random_access_iterator_tag>::__copy_move_b(_BI1, _BI1, _BI2) [with _BI1 = Has_ptr*; _BI2 = Has_ptr*]':
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algobase.h:588:58:   required from '_BI2 std::__copy_move_backward_a(_BI1, _BI1, _BI2) [with bool _IsMove = true; _BI1 = Has_ptr*; _BI2 = Has_ptr*]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algobase.h:598:5:   required from '_BI2 std::__copy_move_backward_a2(_BI1, _BI1, _BI2) [with bool _IsMove = true; _BI1 = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _BI2 = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algobase.h:668:48:   required from '_BI2 std::move_backward(_BI1, _BI1, _BI2) [with _BI1 = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _BI2 = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1848:8:   required from 'void std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1882:25:   required from 'void std::__final_insertion_sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:1968:31:   required from 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algo.h:4707:18:   required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = __gnu_cxx::__normal_iterator<Has_ptr*, std::vector<Has_ptr> >]'
13_31.c++:88:39:   required from here
c:\mingw\lib\gcc\mingw32\6.3.0\include\c++\bits\stl_algobase.h:548:18: error: invalid initialization of non-const reference of type 'Has_ptr&' from an rvalue of type 'std::remove_reference<Has_ptr&>::type {aka Has_ptr}'
      *--__result = std::move(*--__last);
      ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
13_31.c++:42:14: note:   initializing argument 1 of 'Has_ptr& Has_ptr::operator=(Has_ptr&)'
     Has_ptr& operator=(Has_ptr &tmp)
              ^~~~~~~~
C++ 复制构造函数 和交换

评论

1赞 molbdnilo 1/10/2022
非交换复制赋值运算符应采用 const 引用参数。
0赞 David Zhou 1/10/2022
这解决了我的问题XD。
0赞 Sebastian 1/10/2022
考虑使用 std::uniqur_ptr,它与普通指针一样快速且体积小,源代码更小,更易于使用。例如,它会在析构函数处自动删除,或者当您覆盖赋值运算符中的指针时自动删除。
0赞 David Zhou 1/11/2022
感谢您的建议 Sebastion。我应该在实际场景中使用smart_pointer。这是书中的一个练习。

答: 暂无答案