存储作为参数传递的引用的内容
Storing the content of a reference passed as a parameter
我正在用 C++/Qt 编程。这些代码片段是否通过复制正确保存了引用的内容?
有初始化列表:
Foo(const Bar &bar) : bar(bar) { }
Bar bar; //member variable of Foo class
在Setter-方法中:
void TestClass::setName(const QString &name) {
this->name = name;
}
QString name; //member variable of TestClass class
如果是,这是否复制副本?:
void TestClass::setName(const QString name) {
this->name = name;
}
QString name; //member variable of TestClass class
这是否会导致未定义的行为,因为引用的对象可能会提前解构?:
Foo(Bar &bar) : bar(bar) { }
Bar &bar; //member variable of Foo class
存储传递引用的内容是不是不好的风格?传递需要存储的参数的更好方法是什么?
复制开销
这两种方法都会将输入字符串中的数据复制到 class 变量中:
QString name; //member variable of TestClass class
void TestClass::setName1(const QString & name) {
this->name = name;
}
void TestClass::setName2(const QString name) {
this->name = name;
}
在第二个中,QString 的副本将首先在堆栈上创建。您认为按值传递会导致更多的复制开销是正确的。这就是通常不鼓励 class 包含大量数据的 es 的按值传递的原因。
有关按引用传递与按值传递的更多讨论,请参阅 this question。
但是,在这个具体的例子中,开销的差别很小:QString 使用implicit sharing。当您复制 QString(例如使用 operator=)时,它只是创建对相同数据的另一个引用。所以仅仅复制一个 QString —— 即使是一个很长的一个 —— 并不比复制一个引用更密集。
悬挂引用
Foo(Bar &bar) : bar(bar) { }
Bar &bar; //member variable of Foo class
如果原始 bar
被删除而您的 Foo
class 仍然存在(这被称为“dangling reference ”)。 以这种方式使用引用很像使用指针:作为程序员,您需要确保所有涉及的对象都正确管理内存。
如果您担心在您的 Qt 应用程序中悬空 pointers/references,请尝试 QPointer:
A guarded pointer, QPointer, behaves like a normal C++ pointer T *, except that it is automatically set to 0 when the referenced object is destroyed (unlike normal C++ pointers, which become "dangling pointers" in such cases). T must be a subclass of QObject.
您可以像这样重写上面的代码:
Foo(Bar * bar) : bar(bar) { }
QPointer<Bar> bar; //member variable of Foo class
那么你就部分地避免了悬挂指针错误:
Bar * myBar = new Bar();
Foo myFoo(myBar); // myFoo->bar is set to myBar
delete myBar; // myFoo->bar becomes 0
只要您的 class 方法在使用前检查 this->bar
是否为 null,就可以避免对未初始化的内存进行操作。 (或者,如果不这样做,您将很快遇到分段错误,而不是访问已删除的地址时可能出现更微妙的未定义行为。)
我正在用 C++/Qt 编程。这些代码片段是否通过复制正确保存了引用的内容?
有初始化列表:
Foo(const Bar &bar) : bar(bar) { }
Bar bar; //member variable of Foo class
在Setter-方法中:
void TestClass::setName(const QString &name) {
this->name = name;
}
QString name; //member variable of TestClass class
如果是,这是否复制副本?:
void TestClass::setName(const QString name) {
this->name = name;
}
QString name; //member variable of TestClass class
这是否会导致未定义的行为,因为引用的对象可能会提前解构?:
Foo(Bar &bar) : bar(bar) { }
Bar &bar; //member variable of Foo class
存储传递引用的内容是不是不好的风格?传递需要存储的参数的更好方法是什么?
复制开销
这两种方法都会将输入字符串中的数据复制到 class 变量中:
QString name; //member variable of TestClass class
void TestClass::setName1(const QString & name) {
this->name = name;
}
void TestClass::setName2(const QString name) {
this->name = name;
}
在第二个中,QString 的副本将首先在堆栈上创建。您认为按值传递会导致更多的复制开销是正确的。这就是通常不鼓励 class 包含大量数据的 es 的按值传递的原因。
有关按引用传递与按值传递的更多讨论,请参阅 this question。
但是,在这个具体的例子中,开销的差别很小:QString 使用implicit sharing。当您复制 QString(例如使用 operator=)时,它只是创建对相同数据的另一个引用。所以仅仅复制一个 QString —— 即使是一个很长的一个 —— 并不比复制一个引用更密集。
悬挂引用
Foo(Bar &bar) : bar(bar) { }
Bar &bar; //member variable of Foo class
如果原始 bar
被删除而您的 Foo
class 仍然存在(这被称为“dangling reference ”)。 以这种方式使用引用很像使用指针:作为程序员,您需要确保所有涉及的对象都正确管理内存。
如果您担心在您的 Qt 应用程序中悬空 pointers/references,请尝试 QPointer:
A guarded pointer, QPointer, behaves like a normal C++ pointer T *, except that it is automatically set to 0 when the referenced object is destroyed (unlike normal C++ pointers, which become "dangling pointers" in such cases). T must be a subclass of QObject.
您可以像这样重写上面的代码:
Foo(Bar * bar) : bar(bar) { }
QPointer<Bar> bar; //member variable of Foo class
那么你就部分地避免了悬挂指针错误:
Bar * myBar = new Bar();
Foo myFoo(myBar); // myFoo->bar is set to myBar
delete myBar; // myFoo->bar becomes 0
只要您的 class 方法在使用前检查 this->bar
是否为 null,就可以避免对未初始化的内存进行操作。 (或者,如果不这样做,您将很快遇到分段错误,而不是访问已删除的地址时可能出现更微妙的未定义行为。)