在 C++ 中创建和删除对象

Object creation and delete in C++

提问人:Tao 提问时间:10/9/2023 最后编辑:Vlad from MoscowTao 更新时间:10/9/2023 访问量:133

问:

我有下面非常简单的代码来理解C++对象的创建和删除。输出并不是我所期望的。为什么?

class Box{

    public:
        static int count;
        Box(double x, double y, double z) : length(x),width(y),height(z){
            cout << "Box object created with " << length << " " << width << " " << height << endl;
            count++;
            cout << "Count after creation: " << count << endl;
        }
    
        ~Box(){
            cout << "Object deleted!" << endl;
            count--;
            cout << "Count after delete: " << count << endl;
        }        
    
        Box operator + (Box *b){
            return Box(length + b -> length, width + b -> width, height + b -> height);
        }
    
        Box operator + (Box b){
            return Box(length + b.length, width + b.width, height + b.height);
        }
    
    private:
        double length;
        double width;
        double height;
};

int Box::count = 0;

int main()
{    
    Box b1 = Box(1,1,1);
    Box b2 = Box(2,2,2);
    Box b3 = b1 + b2;
    cout << "Here I am expecting 3, but get 2: " << Box::count << endl;

    return 0;    
}

我运行代码并得到以下输出:

Box object created with 1 1 1
Count after creation: 1
Box object created with 2 2 2
Count after creation: 2
Box object created with 3 3 3
Count after creation: 3
Object deleted!
Count after delete: 2
Here I am expecting 3, but get 2: 2.   // I am expecting 3 there, b1, b2 and b3
Object deleted!
Count after delete: 1
Object deleted!
Count after delete: 0
Object deleted!
Count after delete: -1 // how can this been negative!
C++ 对象 运算符重载 传递值 省略

评论

9赞 user17732522 10/9/2023
您违反了 C++ 类设计中最重要的规则即三法则或五法则:如果您定义自己的析构函数,则几乎总是应该定义自己的复制/移动构造函数和赋值运算符。
4赞 HolyBlackCat 10/9/2023
因为你没有涵盖所有构造函数。还有自动生成的复制构造函数和移动构造函数。(您可以推迟学习移动构造函数,并开始阅读有关复制构造函数的信息。无论如何,添加一个都会禁用移动构造函数。
3赞 Some programmer dude 10/9/2023
该语句正在复制初始化对象。但是你没有复制构造函数来增加计数器。Box b3 = b1 + b2;b3
2赞 Eljay 10/9/2023
合成的编译器不做你的自定义。Box(Box const&)++count;
3赞 PaulMcKenzie 10/9/2023
如果您要尝试跟踪正在创建和删除的对象,您应该在消息中打印,而不仅仅是“对象已删除!”和“Box object created with...”。如果您这样做了,您会看到某些要删除的对象与您创建的显示“创建”消息的对象不同。那么你会问自己的问题是“这些被删除的对象是从哪里来的?其他人已经发表了评论,给出了答案。this

答:

3赞 Vlad from Moscow 10/9/2023 #1

程序的意外行为与以下声明有关:operator +

Box operator + (Box b){
    return Box(length + b.length, width + b.width, height + b.height);
}

由于该参数不接受引用的参数,因此在此声明中

Box b3 = b1 + b2;

这相当于

Box b3 = b1.operator +( b2 );

使用默认的复制构造函数为 Operator 函数的参数创建了一个新对象。此对象的值与对象相同。Box bcount2b2

然后由于此声明中的复制/移动省略

Box b3 = b1 + b2;

使用构造函数创建对象b3

    Box(double x, double y, double z) : length(x),width(y),height(z){
        cout << "Box object created with " << length << " " << width << " " << height << endl;
        count++;
        cout << "Count after creation: " << count << endl;
    }

因此,此对象的值等于 。count3

另一方面,使用析构函数删除在 中创建的参数。结果,的值递减,现在等于尽管存在活的对象。operator +count23

而这些消息

Count after creation: 3
Object deleted!        // <===
Count after delete: 2  // <===

关于删除运算符 functon 的局部对象(参数)的报告。

因此,当最后一个对象被销毁时,的值将等于 。count-1

您可以通过以下方式声明operator +

Box operator + (const Box &b){
    return Box(length + b.length, width + b.width, height + b.height);
}

以获得预期的结果。在这种情况下,将不会创建使用默认复制构造函数的类型的冗余对象,因为该对象将通过引用传递给 ,并且不会创建该类型的中间对象。Boxb2operator +Box

否则,您需要为创建的新对象定义显式递增的复制构造函数。count