是否返回一个C ++参考variables的做法,邪恶?
我觉得这有点主观。 我不确定意见是否会一致(我已经看到很多代码片断,其中引用被返回)。
根据对这个问题的评论, 我刚刚问到,关于初始化引用 ,返回一个引用可能是邪恶的,因为[据我所知],更容易错过删除它,这可能导致内存泄漏。
这让我感到担忧,因为我遵循例子(除非我想象的东西),并在less数几个地方这样做…我误解了吗? 它是邪恶的吗? 如果是这样,那么多么邪恶?
我觉得,因为我的指针和引用混杂,加上我是新来的C ++的事实,并彻底混淆什么时候使用,我的应用程序必须是内存泄漏地狱…
另外,我明白,使用智能/共享指针通常被认为是避免内存泄漏的最好方法。
一般来说,返回一个参考是完全正常的,并且一直发生。
如果你的意思是:
int& getInt() { int i; return i; // DON'T DO THIS. }
这是各种各样的邪恶。 分配的堆栈将会消失,而你没有提到任何东西。 这也是邪恶的:
int& getInt() { int* i = new int; return *i; // DON'T DO THIS. }
因为现在客户端最终要做这个奇怪的事情:
int& myInt = getInt(); // note the &, we cannot lose this reference! delete &myInt; // must delete...totally weird and evil int oops = getInt(); delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original
请注意,右值引用仍然是引用,所以所有邪恶的应用程序保持不变。
如果要分配超出函数范围的内容,请使用智能指针(或通常为容器):
std::unique_ptr<int> getInt() { return std::make_unique<int>(0); }
现在客户端存储一个智能指针:
std::unique_ptr<int> x = getInt();
参考也可以访问你知道生命在更高层次上保持开放的东西,例如:
struct immutableint { immutableint(int i) : i_(i) {} const int& get() const { return i_; } private: int i_; };
在这里,我们知道可以返回一个对i_
的引用,因为无论调用我们pipe理类实例的生命周期,所以i_
至less会活到这个长度。
当然,没有错只是:
int getInt() { return 0; }
如果生命应该留给调用者,而你只是在计算价值。
总结:如果在调用之后对象的生命周期不会结束,则可以返回一个引用。
不,不,不,千次不。
什么是邪恶的是做一个dynamic分配的对象的引用,并丢失原来的指针。 当你new
一个对象时,你承担了保证delete
的义务。
但是看看,例如operator<<
: 必须返回一个引用,或者
cout << "foo" << "bar" << "bletch" << endl ;
将无法工作。
您应该返回一个对现有对象的引用,该对象不会立即消失,并且您不希望传输所有权。
永远不要返回一个局部variables或一些这样的引用,因为它不会被引用。
你可以返回一个独立于函数的引用,你不希望调用函数负责删除。 典型的operator[]
函数就是这种情况。
如果你正在创build一些东西,你应该返回一个值或一个指针(普通或智能)。 您可以自由地返回一个值,因为它将进入调用函数中的variables或expression式。 切勿将指针返回到本地variables,因为它将消失。
这不是邪恶的。 就像C ++中的许多事情一样,如果使用正确,它会很好,但是在使用它的时候应该注意许多缺陷(比如返回一个局部variables的引用)。
有好东西可以用它来实现(比如map [name] =“hello world”)
我发现答案不令人满意,所以我会加两分钱。
我们来分析下面的情况:
错误的用法
int& getInt() { int x = 4; return x; }
这显然是错误的
int& x = getInt(); // will refer to garbage
与静态variables一起使用
int& getInt() { static int x = 4; return x; }
这是对的,因为静态variables在整个程序生命周期中都是存在的。
int& x = getInt(); // valid reference, x = 4
在实现Singleton模式时,这也很常见
Class Singleton { public: static Singleton& instance() { static Singleton instance; return instance; }; void printHello() { printf("Hello"); }; }
用法:
Singleton& my_sing = Singleton::instance(); // Valid Singleton instance my_sing.printHello(); // "Hello"
运营商
例如,标准库容器在很大程度上依赖于返回引用的运算符的使用
T & operator*();
可能会在以下使用
std::vector<int> x = {1, 2, 3}; // create vector with 3 elements std::vector<int>::iterator iter = x.begin(); // iterator points to first element (1) *iter = 2; // modify first element, x = {2, 2, 3} now
快速访问内部数据
有时候可以用来快速访问内部数据
Class Container { private: std::vector<int> m_data; public: std::vector<int>& data() { return m_data; } }
用法:
Container cont; cont.data().push_back(1); // appends element to std::vector<int> cont.data()[0] // 1
然而,这可能导致这样的陷阱:
Container* cont = new Container; std::vector<int>& cont_data = cont->data(); cont_data.push_back(1); delete cont; // This is bad, because we still have a dangling reference to its internal data! cont_data[0]; // dangling reference!
“返回一个引用是邪恶的,因为,就我所知,它更容易错过删除它”
不对。 返回引用并不意味着所有权语义。 就是因为你这样做:
Value& v = thing->getTheValue();
…并不意味着你现在拥有v提到的内存;
但是,这是可怕的代码:
int& getTheValue() { return *new int; }
如果你正在做这样的事情,因为“你不需要在那个实例上有一个指针”,那么:1)如果你需要引用,只需要引用指针,2)你最终需要指针,因为你必须匹配新的一个删除,你需要一个指针来调用删除。
有两种情况:
-
const引用 – 良好的想法,有时候,尤其对于重对象或代理类,编译器优化
-
非const引用–bad的想法,有时会打破封装
两者都有相同的问题 – 可能会指向被破坏的对象…
我会build议使用智能指针的许多情况下,你需要返回一个参考/指针。
另请注意以下几点:
有一个正式的规则–C ++标准(如果你感兴趣,可以参考13.3.3.1.4节)声明一个临时的只能绑定到一个const引用 – 如果你试图使用一个非const引用,编译器必须将其标记为一个错误。
它不仅不是邪恶的,而且有时也是必不可less的。 例如,不使用引用返回值就不可能实现std :: vector的[]运算符。
返回引用通常用于运算符在C ++中对于大对象的重载,因为返回值需要复制操作(在perator重载中,我们通常不使用指针作为返回值)
但是返回引用可能会导致内存分配问题。 因为对结果的引用将作为对返回值的引用传递给函数,所以返回值不能是自动variables。
如果要使用返回引用,则可以使用静态对象的缓冲区。 例如
const max_tmp=5; Obj& get_tmp() { static int buf=0; static Obj Buf[max_tmp]; if(buf==max_tmp) buf=0; return Buf[buf++]; } Obj& operator+(const Obj& o1, const Obj& o1) { Obj& res=get_tmp(); // +operation return res; }
这样,你可以安全地使用返回的引用。
但是你总是可以使用指针而不是参考来返回函数中的值。
除了接受的答案:
struct immutableint { immutableint(int i) : i_(i) {} const int& get() const { return i_; } private: int i_; };
我认为这个例子不好 ,如果可能的话应该避免。 为什么? 最后一个悬挂的参考很容易。
举一个例子来说明这一点:
struct Foo { Foo(int i = 42) : boo_(i) {} immutableint boo() { return boo_; } private: immutableint boo_; };
进入危险区域:
Foo foo; const int& dangling = foo.boo().get(); // dangling reference!
我认为使用引用作为函数的返回值比使用指针作为函数的返回值更直接。 其次,使用返回值引用的静态variables总是安全的。
最好的事情是创build对象,并将其作为参考/指针parameter passing给分配此variables的函数。
在函数中分配对象并将其作为参考或指针返回(指针更安全)是不好的,因为在函数块的末尾释放了内存。
函数作为左值(又名,非const引用的返回)应该从C ++中删除。 这非常不直观。 斯科特·迈耶斯(Scott Meyers)希望有这样的行为。
min(a,b) = 0; // What???
这不是一个真正的改进
setmin (a, b, 0);
后者甚至更有意义。
我意识到函数作为左值对于C ++风格的stream是很重要的,但值得指出的是C ++风格的stream是可怕的。 我不是唯一一个这样想的人……正如我记得Alexandrescu写了一篇关于如何做得更好的文章,而且我相信boost也尝试创build一个更好的types安全I / O方法。
Class Set { int *ptr; int size; public: Set(){ size =0; } Set(int size) { this->size = size; ptr = new int [size]; } int& getPtr(int i) { return ptr[i]; // bad practice } };
getPtr函数可以在删除后访问dynamic内存,甚至可以访问空对象。 这可能导致错误的访问exception。 相反,getter和setter应该在返回之前执行和大小validation。
我遇到了一个确实是邪恶的真正的问题。 本质上,开发人员返回对vector中对象的引用。 这是不好的!
我在Janurary写的全部细节: http : //developer-resource.blogspot.com/2009/01/pros-and-cons-of-returing-references.html
关于可怕的代码:
int& getTheValue() { return *new int; }
所以,内存指针确实在返回后丢失了。 但是,如果你使用这样的shared_ptr:
int& getTheValue() { std::shared_ptr<int> p(new int); return *p->get(); }
内存返回后不会丢失,分配后将被释放。