通过一个包含动态内存的类:方法及其效率 [英] Passing a class which holds Dynamic Memory : Methods and their Efficiencies

查看:57
本文介绍了通过一个包含动态内存的类:方法及其效率的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含大量数据的类,名为 HeavyData 。这个类遵循三个规则(它已经重写了复制构造函数,复制赋值运算符和析构函数,以便能够在复制类时正确复制成员变量 someBigAmountOfData 并且能够在不导致内存泄漏的情况下释放类。)



DataManager 类有两个成员变量类型 HeavyData 。 (见下文)



I have a class that holds some big amount of data, called HeavyData. This class Follows the rule of three (It has overridden the copy-constructor, copy-assignment operator and the destructor to be able to copy the member variable someBigAmountOfData correctly when copying the class and to be able to free the class without causing memory leaks).

The DataManager class has two member variables of type HeavyData. (See Below)

class HeavyData
{
public:
    HeavyData();

    HeavyData(const HeavyData& that);
    HeavyData& operator=(const HeavyData& that);
    ~HeavyData();

private:
    void* someBigAmountOfData; //maybe a few hundred bytes (on the heap, of course)
    size_t sizeOfData;
};


class DataManager
{
public:
    DataManager();

    //method 1
    DataManager(HeavyData one, HeavyData two):
        one(one),
        two(two)
    {
    }

    //method 2 (which I think is more effective than method 1)
    DataManager(const HeavyData& one, const HeavyData& two):
        one(one),
        two(two)
    {
    }

private:
    HeavyData one;
    HeavyData two;
};





问题:



DataManager 类有两个构造函数如下:



1. DataManager(HeavyData one,HeavyData two); //方法1

2. DataManager(const HeavyData& one,const HeavyData& two); //方法2



问题在于从上面两个中选择一个构造函数。您认为哪一个更有效?为什么?



认为第二个构造函数(方法2)效率更高。



THE PROBLEM :

The DataManager class has two constructors as follows:

1. DataManager(HeavyData one, HeavyData two); //method 1
2. DataManager(const HeavyData& one, const HeavyData& two); //method 2

The problem is in choosing a constructor from the above two. Which one do you think is more efficient ? And Why ?

In think that the 2nd constructor (method 2) is more efficient.

推荐答案

事实上,这两种方法都不是非常有效。原因是在这两种情况下,必须复制HeavyData对象中的大量数据并且必须分配内存。 (顺便说一句:这几天几百字节不被认为是大量的数据)。相比之下,HeavyData对象本身相当小。是的,第二个构造函数避免将HeavyData对象复制到堆栈上,因此速度稍快。



但是:我在第一句中所说的有一个例外。如果HeavyData包含一个名为lazy copy的智能机制,这将避免大数据块的大部分复制工作,直到数据将要更改为止。许多字符串类使用该技巧来避免不必要的复制操作。但是,从您的代码中看不出您是否打算将此技术用于HeavyData类。它需要你班上的参考计数。



回到主要问题:为什么你的整体课程布局效率不高?因为您打算不仅在DataManager类中保存大数据对象,而且还在其外部保存大数据对象。通常,人们会尝试在DataManager类中保存HeavyData的所有实例,并通过成员函数授予它们访问权限。这比在DataManager类之外构造一个HeavyData对象更有意义(包括分配和填充大数据块),然后通过相对昂贵的复制操作将该对象传输到DataManager。



另一种可行的方法是将指针传递给HeavyData对象,并仅保存指向DataManager类中此类对象的指针。然而,这需要考虑明确的所有权理念:谁是HeavyData对象,creater或DataManager的所有者? (智能指针可能会帮助你解决这个问题。如果你是C ++编程的新手,我不建议你去那条路。)因此,在大多数情况下,让DataManager构造并销毁所有HeavyData对象更容易。
In fact neither of the two methods is very efficient. The reason is that in both cases the huge amount of data in your HeavyData objects must be copied and memory must be allocated. (Btw.: A few hundred bytes are these days not considered a huge amount of data). The HeavyData object itself is in comparison rather small. And yes, the second constructor avoids copying the HeavyData object onto the stack and is hence slightly faster.

BUT: There is one exception to what I said in the first sentence. If the HeavyData contains a smart mechanism called "lazy copy" this would avoid much of the copy work for the big data chunks until the moment that the data is going to be changed. Many string classes use that trick to avoid unnecessary copy operations. It is however not visible from your code whether you are intending to use this technique for the HeavyData class. It would require reference counting in your class.

Back to the main issue: Why is your overall class layout not very efficient? Because you are intending to hold the big-data objects not only inside your DataManager class, but also outside of it. Normally, one would try to hold all instances of HeavyData inside the DataManager class and give access to them via member functions. That makes more sense than to construct a HeavyData object outside the DataManager class (including allocating and filling the big data chunks) and then transfer that object to the DataManager via a relatively expensive copy operation.

Another viable approach would be to pass pointers to the HeavyData object around and hold only pointers to such objects in the DataManager class. That however makes it necessary to think about a clear ownership philosophy: Who is the owner of a HeavyData object, the creater or your DataManager? (Smart pointers might help you in dealing with that problem. I would not recommend to go that path if you are a novice in C++ programming.) Hence, in most cases it is easier to let the DataManager construct and destroy all the HeavyData objects.


对于任何非POD类型的函数参数,您应该总是更喜欢将它们作为const引用传递(即选项2)。即使在DataManager成员初始化之前,按值传递它们也会创建临时值。然后(可能*)将再次复制这些副本。



*:在给定的示例中,编译器可能能够优化掉副本,但是不能保证,更不用说如果你以后更改代码并在构造函数体内添加引用函数参数的实际指令。



可能没有区别(现在),但选项2肯定没有任何缺点,所以这显然是更好的选择。



我同意nv3s建议重新考虑你的重做策略 - 数据复制。



或者,也许,可避免的复制成本可能并不是那么糟糕,而你所询问的意义上的过早优化可能甚至不是必需的。首先检查您是否确实需要优化该代码可能是值得的。
For function arguments of any non-POD type you should always prefer passing them as const reference (i. e. option 2). Passing them by value creates temporaries even before DataManager member initialization. These copies will then (possibly*) be copied again.

*: In the given example, the compiler may be able to optimize away the duplicate copy, but there is no guarantee for that, much less if you later change the code and add actual instructions inside the constructor body that reference the function arguments.

There may be no difference (now), but there is definitely no downside to option 2, so that is clearly the better option.

I do agree though to nv3s advice to rethink your strategy of heavy-data copying.

Or, maybe, the cost of avoidable copying may not be so bad after all, and premature optimization in the sense you ask about may not even be necessary. It may be worth to first check if you actually need to optimize that code.


这篇关于通过一个包含动态内存的类:方法及其效率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆