为什么复制构造函数被调用两次?C++

Why is copy constructor called twice? C++

提问人:Learpcs 提问时间:11/10/2021 最后编辑:Learpcs 更新时间:11/10/2021 访问量:100

问:

因此,我正在创建一个类 BigInteger,以便更好地理解类中对象的构造方式。因此,请考虑以下代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <initializer_list>
#include <exception>
#include <string>
using namespace std;

struct BigInteger
{
    const int base = 1000 * 1000 * 1000; //9 digits.
    bool negative = false;
    vector<unsigned long long> arr;

    BigInteger()
    {
#ifdef _DEBUG
        cout << "default constructor called" << endl;
#endif // DEBUG
    }

    BigInteger(const string& s)
    {
#ifdef _DEBUG
        cout << "string constructor called" << endl;
#endif // DEBUG
        /* crutch but seem to work
    left - left bound for reading the string to exclude the minus sign.
*/
        char left = 0;

        if (s[0] == '-')
        {
            this->negative = true;
            ++left;
        }
        for (int i = s.size() - 9; i >= left; i -= 9)
        {
#ifdef _DEBUG
            if (!(s[i] >= '0' && s[i] <= '9'))
            {
                throw std::invalid_argument("Error: expected a valid number");
            }
#endif // DEBUG
            this->arr.push_back(stoll(s.substr(i, 9)));

        }
        if ((s.size() - left) % 9 != 0)
        {
            this->arr.push_back(stoll(s.substr(left, (s.size() - left) % 9)));
        }
    }

    friend istream& operator>>(istream& in, BigInteger& obj)
    {
#ifdef _DEBUG
        cout << "istream& operator>> called: ";
#endif // DEBUG
        string s;
        in >> s;
        
        obj = BigInteger(s);

        return in;
    }

    friend ostream& operator<<(ostream& out, const BigInteger& obj)
    {
        
        if (obj.negative)
            out << '-';
        out << obj.arr[obj.arr.size() - 1];
        string substr;
        for (int i = obj.arr.size() - 2; i >= 0; --i)
        {
            substr = to_string(obj.arr[i]);
            for (size_t i = 0; i < 9 - substr.size(); i++)
            {
                out << '0';
            }
            out << substr;
        }
        return out;
    }

    BigInteger(const BigInteger& obj) //copy constructor
    {
#ifdef _DEBUG
        cout << "copy constructor called" << endl;
#endif // DEBUG
        this->arr = obj.arr; 
    }

    BigInteger(BigInteger&& obj)  //move constructor
    {
#ifdef _DEBUG
        cout << "move constructor called" << endl;
#endif // DEBUG
        swap(obj);
    }

    ~BigInteger() //destructor
    {
#ifdef _DEBUG
        cout << "destructor called" << endl;
#endif // DEBUG
        arr.clear(); //probably because of RAII, I guess I don't have to write this
    }

    void swap(BigInteger& other)
    {
        std::swap(this->arr, other.arr);
        std::swap(this->negative, other.negative);
    }

    BigInteger& operator=(const BigInteger& rhs)
    {
#ifdef _DEBUG
        cout << "copy assignment called" << endl;
#endif // DEBUG=
        this->arr = rhs.arr;
        return *this;
    }

    BigInteger& operator=(BigInteger&& rhs) noexcept 
    {
#ifdef _DEBUG
        cout << "move assignment called" << endl;
#endif // DEBUG
        swap(rhs);
        return *this;
    }

    BigInteger& operator+=(const BigInteger& rhs)
    {
#ifdef _DEBUG
        cout << "operator+= called" << endl;
#endif // DEBUG
        size_t SIZE = max(this->arr.size(), rhs.arr.size()) + 1;
        this->arr.resize(SIZE);
        for (size_t i = 0; i < SIZE; i++)
        {
            if (i < rhs.arr.size())
            {
                this->arr[i] += rhs.arr[i];
            }
            if (this->arr[i] >= base)
            {
                this->arr[i] %= base;
                ++this->arr[i + 1];
            }
        }
        if (this->arr.back() == 0)
            this->arr.pop_back();
        return *this;
    }

    BigInteger operator+(BigInteger rhs)
    {
        return rhs += *this;
    }
};

int main()
{
    BigInteger a, b;
    cin >> a >> b;
    cout << a + b << endl;
}

所以我尝试输入任何数字(例如 43 和 12)。所以我想了解当我尝试调用事物时会发生什么。那么我期待什么:a + b

  1. 创建右侧对象的副本。
  2. 对此副本执行。operator+=
  3. 返回副本

所以我只期望 1 份。但是不知何故,复制构造函数被调用了两次!输出如下:

default constructor called
default constructor called
istream& operator>> called: 43
string constructor called
move assignment called
destructor called
istream& operator>> called: 12
string constructor called
move assignment called
destructor called
//here the moment where a + b start to executing.
copy constructor called
operator+= called
copy constructor called
destructor called
55
destructor called
destructor called
destructor called

为什么叫了两次? 没有调用任何复制构造函数,我仔细检查了。Operator<<

C++ OOP 重载 COPY-CONSTRUCTOR

评论

1赞 Learpcs 11/10/2021
所以当我离开范围时,第一个副本会被销毁吗?我需要另一份吗?
2赞 Jarod42 11/10/2021
更改为会将副本转换为移动。return rhs += *this;rhs += *this; return rhs;
2赞 Jarod42 11/10/2021
NRVO 不适用于参数,仅适用于局部变量。
2赞 Jarod42 11/10/2021
复制省略Automatic_move_from_local_variables_and_parameters您可能会感兴趣。
1赞 Jarod42 11/11/2021
BigInteger operator+(const BigInteger& rhs) { BigInteger res{tmp}; res += *this; return res; }可能会触发 NRVO( 是一个局部变量)。 (来自之前的评论)无法触发 NRVO,此举是因为是一个参数。resreturn rhs;rhs

答: 暂无答案