提问人:morpheus 提问时间:7/29/2023 最后编辑:anastaciumorpheus 更新时间:8/3/2023 访问量:247
std::string 引用类成员的奇怪行为
Weird behavior with std::string reference class member
问:
给定以下代码:
#include <iostream>
class Foo {
public:
Foo(const std::string& label) : label_(label) {}
void print() {
std::cout << label_;
}
private:
const std::string& label_;
};
int main() {
auto x = new Foo("Hello World");
x->print();
}
我明白了
Hello World!
当我运行它时。如果我这样修改它:
// g++ -o test test.cpp -std=c++17
#include <iostream>
class Base {
public:
Base(const std::string& label) : label_(label) {}
void print() {
std::cout << label_;
}
private:
const std::string& label_;
};
class Derived : public Base {
public:
Derived(const std::string& label) : Base(label) {}
};
int main() {
auto x = new Derived("Hello World");
x->print();
}
我仍然得到:
Hello World
但是如果我这样修改它:
// g++ -o test test.cpp -std=c++17
#include <iostream>
class Base {
public:
Base(const std::string& label) : label_(label) {}
void print() {
std::cout << label_;
}
private:
const std::string& label_;
};
class Derived : public Base {
public:
Derived() : Base("Hello World") {}
};
int main() {
auto x = new Derived();
x->print();
}
我没有得到任何输出。谁能向我解释一下?我是这样编译程序的:
g++ -o test test.cpp -std=c++17
如果它有所作为,这是在 Mac 上。
答:
这三段代码都是不正确的,只是指向临时对象的指针,作为临时对象,你不能保证字符串仍然在 时所指的位置。label_
std::string
"Hello World"
label_
x->print()
如果我们使用优化,编译器会发出悬空的引用警告,奇怪的是,只有这样它才会意识到问题。
在 gcc 13.2 中使用编译器标志:-Wall -Wextra -O3
https://godbolt.org/z/9xjsxhrTT
推测,也许是临时性在声明对象的地方,因此在作用域内,尽管是一个参数,但它可以活得足够长。在第三种情况下,临时直接传递给基构造函数,因此它可能会在 之前被丢弃。,在行动发生的地方,不知道临时的。main
x->print()
main
来自 Java 或 C#,其中除了原始类型之外的所有内容都是通过引用传递的,无需担心,这可能会引起一些混淆,事实是 C++ 并非如此,程序员有责任选择,引用类成员不会保存外部引用数据,如果是暂时的,一旦程序认为适合其内存管理,它就会消失。在这种情况下,如评论部分所述,您应该按值传递数据,而不是通过引用,是 的所有者,它是应该存储它的位置。label_
Foo
评论
const std::string _label
在“正常”情况下,将引用绑定到临时引用会将临时引用的生存期延长到引用的生存期。例如,考虑如下代码:const
std::string foo() { return "Hello World"; }
void bar() {
std::string const& extended_life = foo();
std::cout << extended_life << "\n";
}
返回的 by 是一个临时对象,其生存期通常会在创建它的完整表达式(语句)的末尾过期。string
foo
return
但是,由于我们将其绑定到引用,因此其生存期将延长到引用的生存期,因此在打印出时,行为是完全定义的。const
bar
但是,当所涉及的引用是类的成员时,这并不适用。该标准没有直接解释为什么会这样,但我怀疑这主要是实现的难易问题。
如果我有类似的东西,编译器必须“知道”的声明,并从中获取其返回类型。它还直接“知道”这是对 的引用,因此返回的内容与生存期延长之间的联系相当直接和直接。Foo const &foo = bar();
bar()
foo
const
但是,当您在类中内部存储某些内容时,编译器(至少可能)无法访问该类的内部。例如,在第三种情况下,编译器可以编译时只看到这么多关于和:main
Base
Derived
class Base {
public:
Base(const std::string& label);
void print();
private:
const std::string& label_;
};
class Derived : public Base {
public:
Derived();
};
基于此,编译器无法知道传递给 ctor 的字符串是否以任何方式与 .label_
print()
label_
只有通过分析通过类内容的数据流(在编译调用代码时可能不可用),它才能弄清楚存储的内容或使用方式。要求编译器在代码可能不可用时对其进行分析将导致无法实现的语言。即使所有的源代码都可用,这种关系也可能非常复杂,在某些时候,编译器将不再能够确定正在发生的事情并弄清楚它需要做什么。label_
下一个:按值复制数组
评论
const
const std::string("Hello world")
print()