了解位复制的问题

时间:2015-05-26 12:37:41

标签: c++ count copy-constructor

我想了解以下代码:

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();
}

班蓝莓:

#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++;
};

类蓝莓只是一个具有静态int值blQuantity的类,每次创建一个对象时它都会在构造函数中递增,并且每次对象超出作用域时都会在析构函数中递减。

该程序的读出是:

1
bitCopy 1
-1

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

请不要告诉我我需要复制构造函数。我不是要修复此代码,以便对象计数起作用。我试图理解它是如何工作的,以及为什么blQuantity不是我期望的价值。

1 个答案:

答案 0 :(得分:2)

  

类蓝莓只是一个具有静态int值blQuantity的类,每次创建一个对象时它都会在构造函数中递增,并且每次对象超出作用域时都会在析构函数中递减。

您是否确定每次创建一个?我认为你有一些错过的东西。

blueberry bitCopy(blueberry a)

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

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

您可能还需要移动构造函数和移动赋值运算符 - 请参阅维基百科关于三(或五)&#34;规则的文章。  我在上一段中链接了。

最后blQuantity为-1的原因是因为实际上有{em>两个副本用bitCopy()制作,一个用于参数,一个用于返回值。如果您将其更改为:

blueberry bitCopy (blueberry &a)

即,使用pass-by-reference,只有一个副本,blQuantity之后将为0。如果您再返回值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)。第二个是创建返回值,它是参数的副本,因此对象上的标记现在是#1-copy-copy。这会初始化b中的main()。当test()退出时,参数对象将被销毁#1-copy A bye!

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

这来自b = a中的main()请注意,之前版本的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()的返回值,则只创建一个对象。