我正在尝试实现一个类Foo
,它遵循RAII,类的对象按值返回给客户端,即
class SomeClass {
public:
class Foo {
public:
~Foo() { /* follow raii */ }
private:
friend class SomeClass;
Foo() { /* follow raii */ }
};
Foo getFoo() { return Foo(); }
};
我当前的问题是,有什么方法可以确保在调用Foo
时只构建一个SomeClass::getFoo()
类型的对象?我认为大多数编译器都知道只需要构造一个对象,但我知道在大多数情况下这并不能保证。我能采取更好的方法吗?
我尝试在构造共享指针时返回boost::shared_ptr<Foo>
并仅分配Foo
对象,这很有效。但是,它似乎并不理想,因为它需要堆分配并使接口不太简洁。
谢谢!
澄清
Visual Studio 2005编译器,所以我不认为R-val引用和C ++ 11相关的功能可用。
答案 0 :(得分:2)
你采取了最好的方法。副本(或实际上在C ++ 11中移动)几乎肯定会被编译器忽略。事实上,即使从返回值复制到调用代码中的某个对象,也可能会被忽略。所以这只会调用一个构造函数:
Foo foo = sc.getFoo();
允许省略这两个副本(或移动)的规则是:
当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过直接构造临时对象来省略复制/移动操作进入省略的复制/移动目标
答案 1 :(得分:1)
如果复制构造函数很危险,最好完全禁用它。 虽然大多数编译器会在您的情况下删除副本,但有时可以禁用复制省略,例如-fno-elide-constructors - 如果“相信”复制省略的代码将在这些设置上运行 - 可能是fireworks。
在C ++ 98中,您可以使用boost::optional + boost::in_place - 没有堆分配,因为boost :: optional保留足够的位置。并且保证不会有任何副本。
#include <boost/utility/in_place_factory.hpp>
#include <boost/noncopyable.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <ostream>
using namespace boost;
using namespace std;
struct Foo: private noncopyable
{
explicit Foo(int i)
{
cout << "construction i=" << i << endl;
}
};
void make(optional<Foo> &foo)
{
foo = in_place(11);
}
int main()
{
optional<Foo> foo;
cout << "*" << endl;
make(foo);
cout << "!" << endl;
}
输出是:
*
construction i=11
!
此代码适用于MSVC2005。
另一种选择是对C ++ 98使用移动语义模拟 - Boost.Move。副本被禁用:
#include <boost/move/utility.hpp>
#include <iostream>
#include <ostream>
using namespace std;
class Movable
{
BOOST_MOVABLE_BUT_NOT_COPYABLE(Movable)
bool own_resource;
public:
Movable()
: own_resource(true)
{}
~Movable()
{
cout << (own_resource ? "owner" : "empty") << endl;
}
Movable(BOOST_RV_REF(Movable) x)
: own_resource(x.own_resource)
{
x.own_resource = false;
}
Movable& operator=(BOOST_RV_REF(Movable) x)
{
own_resource = x.own_resource;
x.own_resource = false;
return *this;
}
};
Movable make()
{
return Movable();
}
int main()
{
Movable m = make();
}
输出是:
empty
empty
owner
此代码也适用于MSVC2005。
在C ++ 11中使用以下方法:
struct Foo
{
Foo(const Foo &)=delete;
Foo(Foo &&)=delete;
Foo &operator=(const Foo&)=delete;
Foo &operator=(Foo &&)=delete;
Foo(int){}
};
Foo create()
{
//return Foo{0}; // ERROR: needs Foo(self &&)
return {0};
}
int main()
{
auto &&t=create();
}
Foo只创建一次,它的复制和移动构造函数被删除 - 保证不会有任何副本或移动。