我还是C ++的新手。我发现你可以用两种不同的方式在C ++中实例化一个实例:
// First way
Foo foo;
foo.do_something();
// Second way
Baz *baz = new Baz();
baz->do_something();
两者都没有看到很大差异,可以访问属性。哪个是C ++的首选方式?或者如果问题不相关,我们何时使用哪个以及两者之间有什么区别?
感谢您的帮助。
答案 0 :(得分:4)
这个问题不相关:没有首选的方式,那些只是做不同的事情。
C ++都有值和引用语义。当函数请求值时,表示您将传递整个对象的副本。当它要求引用(或指针)时,您只会传递该对象的内存地址。两种语义都是可转换的,也就是说,如果你得到一个值,你可以获得一个引用或指针,然后使用它,当你得到一个引用时,你可以得到它的值并使用它。举个例子:
void foo(int bar) { bar = 4; }
void foo(int* bar) { *bar = 4; }
void test()
{
int someNumber = 3;
foo(someNumber); // calls foo(int)
std::cout << someNumber << std::endl;
// printed 3: someNumber was not modified because of value semantics,
// as we passed a copy of someNumber to foo, changes were not repercuted
// to our local version
foo(&someNumber); // calls foo(int*)
std::cout << someNumber << std::endl;
// printed 4: someNumber was modified, because passing a pointer lets people
// change the pointed value
}
创建对值的引用(即获取值的指针)是非常非常常见的事情,因为引用非常有用,特别是对于复杂类型,其中传递引用可以显着避免可能代价高昂的复制操作
现在,您将使用的实例化方式取决于您想要实现的目标。您展示的第一种方式使用自动存储;第二个使用堆。
主要区别在于自动存储上的对象会被它们存在的范围破坏(范围大致定义为一对匹配的花括号)。这意味着您不能从常规函数返回对自动存储器上分配的对象的引用,因为在您的函数返回时,该对象将被销毁,并且其内存空间可以在以后的任何时间点重用于任何内容。你的计划。 (在自动存储上分配的对象也有性能优势,因为您的操作系统不必查找可能放置新对象的位置。)
另一方面,堆上的对象继续存在,直到它们被delete
语句显式删除。这有一个与操作系统和平台相关的性能开销,因为你的操作系统需要查找程序的内存来找到一个足够大的未占用位置来创建你的对象。由于C ++不是垃圾收集的,所以你必须在删除堆上的对象时指示您的程序。如果不这样做会导致泄漏:堆上的对象不再被任何变量引用,但未被显式删除,因此在程序退出之前将一直存在。
所以这是一个权衡问题。要么你接受你的价值观不能超过你的功能,要么你接受你必须在某个时候明确delete
它。除此之外,分配对象的两种方式都是有效的,并且按预期工作。
为了进一步参考,自动存储意味着在父范围所在的任何地方分配对象。例如,如果您的班级Foo
包含std::string
,那么只要您分配std::string
对象,Foo
就会存在。
class Foo
{
public:
// in this context, automatic storage refers to wherever Foo will be allocated
std::string a;
};
int foo()
{
// in this context, automatic storage refers to your program's stack
Foo bar; // 'bar' is on the stack, so 'a' is on the stack
Foo* baz = new Foo; // 'baz' is on the heap, so 'a' is on the heap too
// but still, in both cases 'a' will be deleted once the holding object
// is destroyed
}
如上所述,您不能直接泄漏驻留在自动存储上的对象,但是一旦创建它们的作用域被销毁,就无法使用它们。例如:
int* foo()
{
int a; // cannot be leaked: automatically managed by the function scope
return &a; // BAD: a doesn't exist anymore
}
int* foo()
{
int* a = new int; // can be leaked
return a; // NOT AS BAD: now the pointer points to somewhere valid,
// but you eventually need to call `delete a` to release the memory
}
答案 1 :(得分:2)
第一种方式 - “在堆栈上分配” - 通常更快,并且在很多时候都是首选。当函数返回时,构造的对象被销毁。这既是一种祝福 - 没有内存泄漏! - 还有一个诅咒,因为你无法创造一个长寿的物体。
第二种方式 - “在堆上分配”速度较慢,您必须在某个时刻手动删除对象。但它的优点是对象可以继续存在直到你删除它们。
答案 2 :(得分:0)
第一种方法是在堆栈上分配对象(尽管类本身可能有堆分配的成员)。第二种方法是在堆上分配对象,并且必须在以后显式delete
。
它不像Java或C#这样的语言,其中对象始终是堆分配的。
答案 3 :(得分:0)
他们做了很多不同的事情。第一个在堆栈上分配一个对象,第二个在堆上。堆栈分配仅持续使用声明方法的生命周期;堆分配一直持续到删除对象为止。
第二种方法是动态分配对象的唯一方法,但是当你完成它时,你必须记住将这些内存返回给操作系统(通过删除/删除[])增加的复杂性。
答案 4 :(得分:0)
第一种方法是在堆栈上创建对象,当你从创建它的函数返回时,对象就会消失。
第二种方法是在堆上创建对象,对象将一直存在,直到你调用delete foo;
。
如果对象只是一个临时变量,第一种方式更好。如果它是更永久的数据,第二种方式更好 - 只要记住在你最终完成它时调用delete
,这样你就不会在你的堆上建立起来。
希望这有帮助!