C ++从动态分配转换为引用
c++ translate from dynamic allocation to references
我有以下代码:
class A{
public:
virtual do_something() = 0;
}
class B : public A{
public:
virtual do_something() override;
}
void use_a(A *a){
if (a){
a->do_something();
delete a;
}
}
use_a( new B() );
如何将其翻译成参考文献?
注意 do_something()
不是 const
方法。
我认为它可以是这样的:
void use_a(A &&a){
a->do_something();
}
use_a( B() );
但有人告诉我这是不好的风格,必须避免。
右值引用具有移动语义。将 B
移动为 A
.
时效果不佳
使用左值引用:
void use_a(A &a);
B b;
use_a(b);
或模板:
template <typename T>
void use_a(T &&a);
或者,如果不需要是引用,则为智能指针:
void use_a(std::unique_ptr<A> a);
void use_a(std::shared_ptr<A> a);
你的代码有点不安全。
首先,如果 a 为空怎么办?你没有检查它。
其次,如果 a 指向堆栈对象或数据段对象怎么办?你会有意想不到的行为(=在大多数 OS 上崩溃)。
如果您的对象必须动态分配,只需使用 std::shared_ptr
void use_a(std::shared_ptr<A>& a){
a->do_something();
}
很简单,您通过提供一个具体实例将指针转换为引用,即取消引用:
void f(int& i);
f(*(new int)); // do not do this!
问题是 C++ 中的原始指针恰恰是 - 它们没有自动生命周期范围,并且通过转换为左值引用,您建议了一个契约,即实例是具体的,不应被销毁接收器。
int* ptr = new int;
f(ptr);
delete ptr; // otherwise it leaked
现代 C++ 使用 RAII 提供受控的自动生命周期管理,C++11 引入了 unique_ptr
和 shared_ptr
来处理指针。在 C++14 中,我们还有完全避免原始指针的机制。
std::unique_ptr<int> ptr = std::make_unique<int>(/* ctor arguments here */);
f(ptr.get());
// now when ptr goes out of scope, deletion happens automatically.
另见 http://en.cppreference.com/w/cpp/memory/unique_ptr
在任何时候,只有一个 std::unique_ptr
应该拥有给定分配的地址(它假定所有权,并且如果它不是 release
d,它将 delete
退出范围的分配)。
对于引用计数指针:http://en.cppreference.com/w/cpp/memory/shared_ptr
--- 编辑 ---
根据 OP 评论:
首先要注意的是
Pair p = { "one", "two" };
// and
Pair p("one", "two");
Pair p{"one", "two"};
是同义词,在所有情况下,它们通过分配堆栈 space 并调用 Pair::Pair("one", "two")
构造一个 Pair
对象来创建堆栈局部变量 p
.
但是请记住,这是一个堆栈变量 - 它具有自动生命周期并且会在当前作用域结束时过期。
{ Pair p{"one", "two"}; list_add(list, p); } //p is destroyed
理论上可以用
代替
list_add(list, Pair{"one", "two"});
但重要的是 list_add
是否期望 you 保留对象直到您将其从列表中删除...这通常是基于列表的需要指针的函数是预期的。如果它采用非常量引用,它可能会做同样的事情。
回答你原来的问题 post::
struct A { virtual void doSomething() {} };
struct B : public A { virtual void doSomething() override() {} };
void useDoSomethingInterface(A& a) {
a.doSomething();
}
int main() {
A a;
B b;
useDoSomethingInterface(a);
useDoSomethingInterface(b);
}
考虑以下因素:
void list_add(IList& list, Pair& pair) {
pair.next = list.head;
list.head = &pair; // << BAD NEWS
}
void badness(IList& list) {
list_add(list, Pair("hello", "world"));
}
void caller() {
IList list;
badness(list);
// list.head now points to a destroyed variable on the stack
C++ 中的 C 指针是原始的机器级指针。他们不引用计数。并且 C++ 对象实例具有固定且定义明确的生命周期:直到范围结束。
但是,如果 list_add
按值获取其数据
void list_add(IList& list, Pair pair)
那我们就没事了。我们创建的临时 Pair 将不得不复制一次以创建 pair
然后再次复制到列表中,这是一个耻辱但至少它不会崩溃。
我有以下代码:
class A{
public:
virtual do_something() = 0;
}
class B : public A{
public:
virtual do_something() override;
}
void use_a(A *a){
if (a){
a->do_something();
delete a;
}
}
use_a( new B() );
如何将其翻译成参考文献?
注意 do_something()
不是 const
方法。
我认为它可以是这样的:
void use_a(A &&a){
a->do_something();
}
use_a( B() );
但有人告诉我这是不好的风格,必须避免。
右值引用具有移动语义。将 B
移动为 A
.
使用左值引用:
void use_a(A &a);
B b;
use_a(b);
或模板:
template <typename T>
void use_a(T &&a);
或者,如果不需要是引用,则为智能指针:
void use_a(std::unique_ptr<A> a);
void use_a(std::shared_ptr<A> a);
你的代码有点不安全。
首先,如果 a 为空怎么办?你没有检查它。
其次,如果 a 指向堆栈对象或数据段对象怎么办?你会有意想不到的行为(=在大多数 OS 上崩溃)。
如果您的对象必须动态分配,只需使用 std::shared_ptr
void use_a(std::shared_ptr<A>& a){
a->do_something();
}
很简单,您通过提供一个具体实例将指针转换为引用,即取消引用:
void f(int& i);
f(*(new int)); // do not do this!
问题是 C++ 中的原始指针恰恰是 - 它们没有自动生命周期范围,并且通过转换为左值引用,您建议了一个契约,即实例是具体的,不应被销毁接收器。
int* ptr = new int;
f(ptr);
delete ptr; // otherwise it leaked
现代 C++ 使用 RAII 提供受控的自动生命周期管理,C++11 引入了 unique_ptr
和 shared_ptr
来处理指针。在 C++14 中,我们还有完全避免原始指针的机制。
std::unique_ptr<int> ptr = std::make_unique<int>(/* ctor arguments here */);
f(ptr.get());
// now when ptr goes out of scope, deletion happens automatically.
另见 http://en.cppreference.com/w/cpp/memory/unique_ptr
在任何时候,只有一个 std::unique_ptr
应该拥有给定分配的地址(它假定所有权,并且如果它不是 release
d,它将 delete
退出范围的分配)。
对于引用计数指针:http://en.cppreference.com/w/cpp/memory/shared_ptr
--- 编辑 ---
根据 OP 评论:
首先要注意的是
Pair p = { "one", "two" };
// and
Pair p("one", "two");
Pair p{"one", "two"};
是同义词,在所有情况下,它们通过分配堆栈 space 并调用 Pair::Pair("one", "two")
构造一个 Pair
对象来创建堆栈局部变量 p
.
但是请记住,这是一个堆栈变量 - 它具有自动生命周期并且会在当前作用域结束时过期。
{ Pair p{"one", "two"}; list_add(list, p); } //p is destroyed
理论上可以用
代替list_add(list, Pair{"one", "two"});
但重要的是 list_add
是否期望 you 保留对象直到您将其从列表中删除...这通常是基于列表的需要指针的函数是预期的。如果它采用非常量引用,它可能会做同样的事情。
回答你原来的问题 post::
struct A { virtual void doSomething() {} };
struct B : public A { virtual void doSomething() override() {} };
void useDoSomethingInterface(A& a) {
a.doSomething();
}
int main() {
A a;
B b;
useDoSomethingInterface(a);
useDoSomethingInterface(b);
}
考虑以下因素:
void list_add(IList& list, Pair& pair) {
pair.next = list.head;
list.head = &pair; // << BAD NEWS
}
void badness(IList& list) {
list_add(list, Pair("hello", "world"));
}
void caller() {
IList list;
badness(list);
// list.head now points to a destroyed variable on the stack
C++ 中的 C 指针是原始的机器级指针。他们不引用计数。并且 C++ 对象实例具有固定且定义明确的生命周期:直到范围结束。
但是,如果 list_add
按值获取其数据
void list_add(IList& list, Pair pair)
那我们就没事了。我们创建的临时 Pair 将不得不复制一次以创建 pair
然后再次复制到列表中,这是一个耻辱但至少它不会崩溃。