C++ 静态/线程/自动/动态存储持续时间对象中有四种类型的变量。但为了保持讨论简单,让我们集中讨论两个最常见的(自动/动态)。
自动变量。
它们在声明时创建,并在超出范围时销毁。
void func1();
{
MyClass x; // Creates an object of MyClass.
// DoStuff
} // x is destroyed because it goes out of scope.
相同的规则适用于对象中的变量。不同之处在于范围是包含它们的对象的生命周期。
class AnotherClass
{
MyClass member1;
MyClass member2;
};
void test()
{
AnotherClass ac; // The object ac is created here.
// The members (member1 and member2) are created at the same time.
// and they live as long as the object ac lives.
} // Everything destroyed here.
// If we dynanically allocate ac (see below).
// then the members will live until the parent object is destroyed.
// Which happens when you call delete.
动态变量。
这些是通过使用关键字创建的,new
并且在使用关键字明确删除它们之前一直存在delete
。
void func2()
{
MyClass& y = new MyClass(); // Creates a dynamic object
// This will live until it is explicitly deleted.
} // You forgot to call delete the object still exists but there is no variable
// pointing at it (so we have lost all references and it is not leaked).
直接使用动态变量并不常见;在大多数情况下,您将它们包装在会自动调用delete
您的智能指针中。最简单的智能指针是std::shared_ptr
. 它基本上是一个指针周围的引用计数包装器。因此,当您在引用计数周围复制它时,随着变量被破坏,计数会增加,当它达到零时,它会被破坏。
void func3()
{
std::shared_ptr<MyClass> z = new MyClass(); // This is the closes we have to a Java variable
// It is reference counted.
// When no more values exist then it is deleted.
std::shared_ptr<MyClass> a = z; // a and z point (refer) to the same object.
}
// The object is destroyed (because both a and z are automatic variables).
// At this point they have gone out of scope decrementing the count.
// The count reached zero and the object was destroyed (with delete)
参考
术语引用的问题在于它在不同语言中重叠并且在两种语言中意味着不同的东西。
在 Java 中,所有变量都是引用(忽略 Java 原始类型)。某处有一个对象(由 new 创建),您可以拥有一堆引用该对象的变量。对于大多数 C/C++ 程序员来说,这听起来就像一个指针。
void myJavaFunc()
{
MyClass a = new MyClass;
MyClass b = a; // both a and b refer to the same variable.
//
b.doStuff(); // Both 'a' and 'b' refer to the same object.
// So anything you do via b is visable to 'a'
}
GC 会跟踪对象有多少引用,当它达到零时,它会清理它。
在 C++ 中,引用是对现有对象的另一个名称(它是别名)。一旦创建了引用,就不能更改它正在别名的对象。
void MyCPPFunc()
{
MyClass a; // Creates a local object.
MyClass& b = a; // Creates a reference variable b that is an alias for a.
//
b.doStuff(); // Both 'a' and 'b' refer to the same object.
// So anything you do via b is visable to 'a'
}
差异。
在 C++ 中变量和引用不能为 NULL。
在 C++ 中,引用不能被重新定位(指向另一个对象)。
笔记。在 C++ 中,指针可以为 NULL(但不是对象)。
回到问题:
问题一:
如果我写向量列表,这也是参考吗?但是如果它是一个引用,为什么当我将一个对象传递给一个函数时,它又被复制了一次?列表中究竟存储了什么?
vector<int> list; // This creates an object in the local scope.
vector<int>& A = list; // This creates a reference to the list object.
vector<int> B = list; // This creates a new object B that is a copy of list.
当您调用函数时,您可以使用对象或引用作为参数类型。
void myDoStuffOne(vector<int> p1)
{
// Here p1 is an object.
// because it is an object it is created as a copy of the parameter
// that was passed at the call point.
//
// Since they are different objects manipulating p1 does not change
// the value of the original variable.
}
void myDoStuffTwo(vector<int>& p1)
{
// Here p1 is a reference.
// because it is an reference it is alias of the parameter that was passed.
// at the call point. So it refers to the same object.
//
// Any action on this object is the same as manipulating the original
// object and you can see the difference when the function returns.
}
// also worth noting are const reference parameters.
void myDoStuffThress(vector<int> const& p1)
{
// Here p1 is a const reference.
// Just like a normal reference except the function is not allowed to
// mutate the state of the object (also useful for passing temporary objects).
}
问题2:
当我返回一个对象(例如,使用函数头向量 make_vector(){...})时,我是否返回了对本地对象的新克隆的引用?
所以像这样的函数:
std::vector<int> make_vector()
{
std::vector<int> result;
// fill vector in some way.
return result;
}
当您按值返回对象时,您必须将对象复制出函数。所以从技术上讲,是的,应该制作一份副本。(请注意,如果您要返回本地对象,则必须按值返回。如果要返回对象的成员,则可以通过引用(或 const 引用)返回)。
但实际上是复制品。答案总是否定的。
在 C++03 中,编译器经过高度优化并消除了返回副本(技术称为 RVO 和 NRVO 优化)。生成的对象是在函数外部需要它的地方创建的。这在 99% 的时间内都有效(由统计数据组成,但效率很高)。
在 C++11 中添加了一项新技术,可以解决剩下的 1% 的问题。当对象按值返回时,其内容不会被复制而是被移动(这通常涉及复制几个指针,而且通常非常简单)。