了解位复制的问题

Understanding the issues of bit copying

我正在尝试理解以下代码:

blueberry bitCopy(blueberry a) {
  cout << "bitCopy " << blueberry::blQuantity << endl;
  return a;
}

void howMany() {
  cout << blueberry::blQuantity << endl;
}

int main() {
  blueberry firstBl;
  howMany();
  bitCopy(firstBl);
  howMany();
}

class蓝莓:

#ifndef header_h
#define header_h
#pragma once
#include <string>
#include <iostream>

using namespace std;

class blueberry {
private:
static int blQuantity;
public:
    blueberry();
    ~blueberry() {
    blQuantity--;
    }
    friend blueberry bitCopy(blueberry a);
    friend void howMany();
}; 

#endif

int blueberry::blQuantity = 0;

blueberry::blueberry() {
    blQuantity++;
};

class 蓝莓只是一些 class,它有一个静态 int 值 blQuantity,每次创建对象时在构造函数中递增,每次对象退出时在析构函数中递减范围。

从这个程序中读出的是:

1
bitCopy 1
-1

我原以为最后是 0 而不是 -1。有人可以解释一下吗?

请不要告诉我我需要一个复制构造函数。我不是要修复此代码以使对象计数有效。相反,我试图了解它是如何工作的以及为什么 blQuantity 不是我期望的值。

class blueberry is just some class that has a static int value blQuantity that increments in the constructor each time an object is created, and decrements in the destructor each time an object goes out of scope.

你确定每次都创建一个吗?我想你错过了什么。

blueberry bitCopy(blueberry a)

这是按值传递;即,blueberry a 这里是 提交给 bitCopy() 的副本。这会调用您尚未定义的蓝莓 copy constructor。因此,编译器会为您创建一个简单的对象,它会从原始对象复制任何成员值——但不会增加任何内容。如果你想要那个,你必须定义:

blueberry::blueberry (const blueberry&) // copy constructor
blueberry& operator= (const blueberry&) // copy assignment operator

您可能还需要移动构造函数和移动赋值运算符——请参阅有关 "rule of three (or five)" 的维基百科文章 我在上面的段落中链接了。

之所以blQuantity最后是-1是因为实际上两个用[=20=制作的副本,一个用于参数,一个用于return 值。如果将其更改为:

blueberry bitCopy (blueberry &a)

即引用传递,只有一份,之后blQuantity为0。如果您随后将 return 值设置为 void,则不会生成任何副本,并且 blQuantity 应该为 1。


这里演示一下拷贝构造函数和operator=(拷贝赋值运算符)的作用:

#include <iostream>
#include <string>

using namespace std;

class A {
    public:
        string x;
        A (string s) : x(s) {
            cout << "A con " << "(" << x << ")\n";
        }
        A (const A& other) : x(other.x) {
            x.append("-copy");
            cout << "A copy " << "(" << x << ")\n";
        }
        A& operator= (const A& other) {
            x = other.x;
            x.append("[=]");
            cout << "A assign " << "(" << x << ")\n";
            return *this;
        }
        ~A () { cerr << x << " A bye!\n"; }
};


A test (A a) {
    return a;
}


int main (void) {
    A a("#1");
    cout << "test()\n";
    A b = test(a);
    cout << "Copy assign:\n";
    b = a;
    cout << "Exiting...\n";
    return 0;
}

我将单步执行此输出:

A con (#1)
test()
A copy (#1-copy)
A copy (#1-copy-copy)
#1-copy A bye!

第一行来自A a("#1")。最后三行是 A b = test(a) 的结果。第一个是复制参数,A test (test a)。第二个是创建 return 值,它是参数的副本,因此对象上的标记现在是 #1-copy-copy。在 main() 中初始化 btest()退出时,参数对象被销毁,#1-copy A bye!.

Copy assign:
A assign (#1[=])

这来自 main() 中的 b = a注意b之前的版本没有被销毁。这是因为复制赋值是为了将一个对象变成另一个对象的副本;两个对象都没有被销毁,但目标对象的内容可能已更改,因此 b 的标签现在是 #1[=]。如此不出所料:

Exiting...
#1[=] A bye!
#1 A bye!

当程序结束时,ab被销毁。

如果将 test() 的签名更改为:

A& test (A &a) 

你会得到这个输出:

A con (#1)
test()
A copy (#1-copy)
Copy assign:
A assign (#1[=])
Exiting...
#1[=] A bye!
#1 A bye!

只创建了两个对象,a 通过构造函数和 b 通过复制 con;他们两个直到最后都没有被摧毁。如果您随后不使用 test() 的 return 值,只会创建一个对象。