C++完全是关于内存所有权
Aka" Ownership Semantics "
一块动态分配的内存的所有者负责释放该内存.所以这个问题真的变成了拥有记忆的人.
在C++中,所有权都是由RAW指针包含在内部的类型记录的,因此在一个好的(IMO)C++程序中,很少见[RARE并非永远]看到RAW指针传递(因为RAW指针没有推断的所有权因此我们不能告诉谁拥有记忆,因此如果没有仔细阅读文件,你无法分辨谁负责所有权).
相反,很少看到RAW指针存储在类中,每个RAW指针都存储在自己的SMART指针包装器中.(注意:如果你没有一个对象,你不应该存储它,因为你不知道什么时候它会超出范围并被销毁.)
所以问题是:
让我们为每个答案保留一种语义所有权,这样他们就可以单独上下投票
从概念上讲,智能指针很简单,而且简单易用.我已经看过许多尝试过的实现,但总是以某种方式打破它们,这对于随意使用和示例来说并不明显.因此,我建议始终使用经过良好测试的"智能指针",而不是自己动手.std :: auto_ptr或其中一个提升智能指针似乎涵盖了我的所有需求.
单身人士拥有该物品.
但允许转让所有权.
用法:
======
这允许您定义显示所有权显式转移的接口.
单身人士拥有该物品.
不允许转让所有权.
用法:
======
用于显示明确的所有权.
对象将被析构函数或显式重置时销毁.
多个所有权.
这是一个简单的引用计数指针.当引用计数达到零时,对象被销毁.
用法:
======
当对象可以有多个owers,其生命周期无法在编译时确定.
与shared_ptr <T>一起使用.
在指针循环可能发生的情况下.
用法:
======
用于在仅循环维护共享引用计数时停止保留对象的周期.
我编写了一个静态工厂方法,它返回从另一个数据对象填充的新Foobar对象.我最近一直沉迷于所有权语义,我想知道我是否通过这种工厂方法返回一个正确的信息unique_ptr.
class Foobar {
public:
static unique_ptr<Foobar> factory(DataObject data);
}
Run Code Online (Sandbox Code Playgroud)
我的目的是告诉客户端代码他们拥有指针.没有智能指针,我只会回来Foobar*.但是,我想强制删除这个内存以避免潜在的错误,所以这unique_ptr似乎是一个合适的解决方案.如果客户端想要延长指针的生命周期,他们只需在调用.release()后调用unique_ptr.
Foobar* myFoo = Foobar::factory(data).release();
Run Code Online (Sandbox Code Playgroud)
我的问题分为两部分:
unique_ptr而不是原始指针这是一个"坏习惯" 吗?这里记录了Rust的移动语义的一个很好的例子:在Rust By Example网站上的Rust Move Semantics.
我对两个案例都有基本的了解.第一个是原语如何具有新别名,原始仍然可以使用,因为最终结果是看到i32利用Copy特征的副本.这对我来说很有意义.
此外,由于许多好的理由,第二个示例在具有多个引用i32堆上的别名方面是有意义的.Rust强制执行所有权规则,因此现在无法使用原始别名创建新绑定.这有助于防止数据争用,双重释放等.
但似乎还有第三个案例没有被谈到.Rust如何实现不实现Copy特征的堆栈分配结构的移动? 使用以下代码对此进行说明:
#[derive(Debug)]
struct Employee{
age: i32,
}
fn do_something(m: Employee){
println!("{:?}", m);
}
fn main() {
let x = Employee {
age: 25,
};
do_something(x);
//compiler error below because x has moved
do_something(x);
}
Run Code Online (Sandbox Code Playgroud)
我知道:在上面的例子中,Rust会Employee在堆栈上分配.上述结构不实现Copy特征,因此在分配给新别名时不会被复制.这对我来说非常混乱,因为如果Employee结构被分配在堆栈上并且也没有实现Copy特性在哪里/如何移动?它是否在物理上移动到do_something()堆栈框架?
在解释这个难题时,我们对任何帮助表示赞赏.
我有一个实现的草图:
trait Listener {
fn some_action(&mut self);
fn commit(self);
}
struct FooListener {}
impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}
fn commit(self) {
println!("{:?}", "Commit");
}
}
struct Transaction {
listeners: Vec<Box<Listener>>,
}
impl Transaction {
fn commit(self) {
// How would I consume the listeners and call commit() on each of them?
}
}
fn listener() {
let transaction = Transaction {
listeners: vec![Box::new(FooListener {})],
};
transaction.commit();
}
Run Code Online (Sandbox Code Playgroud)
我可以Transaction在它们上面使用侦听器,当事务发生时会调用侦听器.既然Listener是特质,我会存储一个Vec<Box<Listener>> …
我有一些不可复制的类型和一个消耗和(可能)生成它的函数:
type Foo = Vec<u8>;
fn quux(_: Foo) -> Option<Foo> {
Some(Vec::new())
}
Run Code Online (Sandbox Code Playgroud)
现在考虑一种在概念上非常类似的类型Box:
struct NotBox<T> {
contents: T
}
Run Code Online (Sandbox Code Playgroud)
我们可以编写一个临时移出内容的函数,NotBox并在返回之前放回一些东西:
fn bar(mut notbox: NotBox<Foo>) -> Option<NotBox<Foo>> {
let foo = notbox.contents; // now `notbox` is "empty"
match quux(foo) {
Some(new_foo) => {
notbox.contents = new_foo; // we put something back in
Some(notbox)
}
None => None
}
}
Run Code Online (Sandbox Code Playgroud)
我想编写一个与Boxes 一起使用的类似函数,但编译器不喜欢它:
fn baz(mut abox: Box<Foo>) -> Option<Box<Foo>> {
let foo = *abox; // now …Run Code Online (Sandbox Code Playgroud) 我主要是C++程序员,我已经习惯了具有类似于类模板std::unique_ptr,std::shared_ptr表达我的对象的所有权等.Delphi的标准库中是否有类似的东西?在编写代码时,是否有任何表达对象所有权的最佳实践?
编辑:由于C++ 11成为标准,有两个轻量级助手类,std::shared_ptr和std::unique_ptr.
如果我创建一个类型的变量std::shared_ptr<int>,它表示一个指向具有共享所有权的int的指针:引擎计数引用,当引用计数达到零时,指针将自动释放.这种类型表示一种"共享所有权",其中许多对象共同负责在完成资源时销毁资源.
相反,std::unique_ptr表达单一所有权.当unique_ptr超出范围时,将自动释放资源.std :: unique_ptr无法复制:一次只能有一个对象拥有此资源,并且只有一个对象负责清理对象.
将这些轻量级类与一个指向int的裸指针进行对比,它可以表示共享所有权,唯一所有权,或者它可以只是对其他地方的对象的引用!类型告诉你什么.
我的问题是:由于Delphi支持对对象的引用,是否有任何机制可以明确说明"我是这个对象的唯一所有者,当我完成它时,我会释放它",vs"我只是保留一个引用此对象以便与它进行交互,但是其他人会清理它"vs"我与许多其他对象共享此对象,并且最后拥有它的任何人都可以清理它."
我知道Collections.Generics有不同的集合,比如TListvs TObjectList,其中TObjectList将释放存储在其中的成员,但TList不会.你可以说TObjectList"拥有"它的元素,而TList则没有.这是我的问题的本质,真的.在设计我自己的课程时,有没有办法在语言中直接表达这些所有权问题?或者是否有开发人员常见的最佳实践/命名约定?
假设有一个对象A拥有一个对象B via std::unique_ptr<B>.进一步B保持对A的原始指针(弱)引用.然后A的析构函数将调用B的析构函数,因为它拥有它.
在B的析构函数中访问A的安全方法是什么?(因为我们也可能在A的析构函数中).
一种安全的方法是在A的析构函数中明确重置对B的强引用,以便以可预测的方式销毁B,但一般的最佳实践是什么?
我想编写一个通用函数,它接受任何类型的字符串(&str/ String)以方便调用者.
函数内部需要a String,所以如果调用者调用函数,我也想避免不必要的重新分配String.
foo("borrowed");
foo(format!("owned"));
Run Code Online (Sandbox Code Playgroud)
接受参考我知道我可以使用foo<S: AsRef<str>>(s: S),但另一种方式呢?
我认为基于的泛型参数ToOwned可能有用(适用于&str,我假设它是无操作String),但我无法弄清楚确切的语法.
我希望我的类的析构函数删除整个对象,除了其中一个成员,在其他地方删除.首先,这是完全不合理的吗?假设不是,我该怎么做?我认为创建一个带有空体的析构函数会阻止所有成员被删除(因为析构函数不会做任何事情),但似乎并非如此.
我知道std::unique_ptr在对象只有一个所有者std::shared_ptr时使用,而在对象有多个所有者时使用。成为对象的唯一所有者意味着什么?
成为唯一所有者是否意味着没有其他人可以看到该物体?
get()将指针作为引用传递时,可以使用该方法返回原始指针吗?
我觉得,如果必须使用get(),那么我要么通过使用转移了所有权std::move,要么使用了共享指针。