实例化方法中所需的对象有什么理由,而不是使对象成为类成员?
例如,在下面的示例代码中,我有一个类ClassA,我希望从另一个类中使用,例如USer1,它具有指向classA的对象的指针作为成员变量并在其构造函数中实例化,另一方面User2,它在使用它之前在方法中实例化classA的对象。有什么理由以单向与另一种方式做到这一点?
class ClassA
{
public:
void doStuff(void){ }
};
//
// this class has ClassA as a member
//
class User1
{
public:
User1()
{
classA = new ClassA();
}
~User1()
{
delete classA;
}
void use(void)
{
classA->doStuff();
}
private:
ClassA *classA;
};
//
// this class uses ClassA only in a method
//
class User2
{
public:
void use(void)
{
ClassA *classA = new ClassA();
classA->doStuff();
delete classA;
}
};
int main(void)
{
User1 user1;
user1.use();
User2 user2;
user2.use();
return 0;
}
答案 0 :(得分:2)
使其成为集体成员的好处是:
作为旁注,如果你只是在构造函数和析构函数中使用new和delete进行实例化和删除,它应该不是一个指针,只是一个成员实例,然后摆脱新的和删除。
IE
class User1
{
public:
void use(void)
{
classA.doStuff();
}
private:
ClassA classA;
};
有时情况并非如此,例如,当堆栈上分配的类很大,或者您希望保持类的占用空间尽可能小时。但这些是例外而不是规则。
还有其他需要考虑的事情,如内存碎片,访问连续内存块的优势,以及如何在目标系统上分配内存。没有银子弹,只有一般建议,对于任何特定的程序,您需要测量和调整以获得最佳性能或克服特定程序的限制。
内存碎片即使你有大量内存可用,但是单个块的大小非常小,当你尝试分配大量内存时会出现内存错误。这通常是由创建和销毁各种大小的不同对象引起的,其中一些对象保持活力。如果您的系统存在内存碎片问题,我建议您彻底分析如何创建对象,而不是担心成员是否会影响系统。但是,以下是当您遇到内存碎片时四种不同场景如何发挥作用的细分:
访问连续内存的优点是缓存未命中最小化,所以我的感觉是将对象作为值成员会更快,但是由于很多事情依赖于许多其他变量,这可能是完全错误的。与性能一样,衡量。
内存通常与特定边界对齐,例如4字节对齐,或2个块的功率。因此,当您分配其中一个时,取决于对象的大小,它可能会占用比您预期更多的内存,如果您分配的对象包含任何成员,如果它是值成员,则可能会显着改变类的内存占用,或者如果它根本不会增加它,而拥有指向它的指针肯定会增加指针大小的足迹,这可能会导致显着增加。在堆或堆栈上创建类不会影响使用类的大小。与往常一样,如果它会影响您的程序,您需要在目标系统上进行测量,以查看效果将会是什么。
如果构造函数/析构函数执行某些操作(例如文件句柄,打开文件和关闭文件),那么您可能只想在函数中使用它。但又一次,指针通常不是必需的。
void use(void)
{
ClassA classA;
classA.doStuff();
} //classA will be destructed at end of scope
答案 1 :(得分:2)
首先,没有理由在任何一个类中都有指针。如果我们在User1
中使用value semantics,则不需要构造函数或析构函数,因为编译器生成的函数就足够了。这会将User1
更改为:
class User1
{
public:
void use(void)
{
classA.doStuff();
}
private:
ClassA classA;
};
同样,如果我们在User2
中使用值语义,那么它将成为:
class User2
{
public:
void use(void)
{
ClassA classA;
classA.doStuff();
}
};
现在,您是否希望将ClassA
作为成员,或者您是否应该在功能中使用它是一个设计问题。如果该类将使用并更新ClassA
,那么它应该是成员。如果你只是需要在函数中做某事,第二种方法是可以的。
如果你打算多次调用创建ClassA
的函数,那么将它作为成员可能是有益的,因为你只需要构造它一次就可以在函数中使用它。相反如果你想拥有很多物品,但你几乎没有调用过这个功能,那么在需要时创建ClassA
可能会更好,因为这会节省空间。
实际上,您需要对此进行分析,以确定哪种方式会更好。我们程序员对什么是更快的判断是错误的,应该让剖析器告诉我们是否需要改变一些东西。对于使用堆分配的指针使用值语义的一些事情通常更快。我们弄错了的一个例子就是排序。如果N很小,那么使用O(n^2)
的冒泡排序比O(n log n)
的快速排序要快。这个si的另一个例子是在Hurb Sutter talk从46:00开始提出的。他表示,在插入和删除中间时,使用std::vector
比std::list
更快,因为std::vector
非常缓存,而std::list
则不是。{/ p >