我有一个包含不可复制句柄的C ++类。但是,该类必须具有复制构造函数。所以,我实现了一个将句柄的所有权转移到新对象(如下所示),
class Foo
{
public:
Foo() : h_( INVALID_HANDLE_VALUE )
{
};
// transfer the handle to the new instance
Foo( const Foo& other ) : h_( other.Detach() )
{
};
~Foo()
{
if( INVALID_HANDLE_VALUE != h_ )
CloseHandle( h_ );
};
// other interesting functions...
private:
/// disallow assignment
const Foo& operator=( const Foo& );
HANDLE Detach() const
{
HANDLE h = h_;
h_ = INVALID_HANDLE_VALUE;
return h;
};
/// a non-copyable handle
mutable HANDLE h_;
}; // class Foo
我的问题是标准的复制构造函数采用const-reference,我正在修改该引用。所以,我想知道哪个更好(以及为什么):
非标准的复制构造函数:
Foo( Foo& other );
'谎言'的复制构造函数:
Foo( const Foo& other );
编辑:
DuplicateHandle()
仅适用于特定类型的句柄。这不是其中的一个。无法复制,复制或克隆此句柄。
有几个人指出我错误地认为它是一个非标准的复制构造函数,而std::auto_ptr
就是这样做的。我认为这可能是要走的路。但是,每当我使用类时,当我使复制ctor取非const值时,我最终会收到警告。例如:
namespace detail {
class Foo { ... };
};
class Buzz
{
public:
typedef detail::Foo Fuzz;
Fuzz bar() const { return Fuzz(); }; // warning here
};
warning C4239: nonstandard extension used : 'argument' : conversion from 'Foo' to 'Foo &'
1> A non-const reference may only be bound to an lvalue; copy constructor takes a reference to non-const
有人可以建议我应该对他们做些什么吗?
EDIT2:
每个人似乎都在引导我走向std::auto_ptr<>
的做事方法。所以,我看了那里,它使用一个中间结构来解决我在第一次编辑中描述的问题。这是我提出的解决方案。
class Foo;
struct Foo_ref
{
explicit Foo_ref( Foo& other ) : ref_( other ) {};
Foo& ref_;
private:
const Foo_ref& operator=( const Foo_ref& );
}; // struct Foo_ref
class Foo
{
public:
Foo() : h_( INVALID_HANDLE_VALUE )
{
};
// transfer the handle to the new instance
Foo( Foo_ref other ) : h_( other.ref_.Detach() )
{
};
~Foo()
{
if( INVALID_HANDLE_VALUE != h_ )
CloseHandle( h_ );
};
operator Foo_ref()
{
Foo_ref tmp( *this );
return tmp;
};
// other interesting functions...
private:
/// disallow assignment
const Foo& operator=( const Foo& );
HANDLE Detach()
{
HANDLE h = h_;
h_ = INVALID_HANDLE_VALUE;
return h;
};
/// a non-copyable handle
HANDLE h_;
}; // class Foo
它在警告级别4上干净地编译并且似乎有效。请让我知道它是否比我原来的帖子更不负责任。
答案 0 :(得分:6)
两者都很可怕。如果您的类具有不可复制的成员变量,那么您的类是不可复制的。
如果你的类是不可复制的是非常不可接受的,一个解决方法就是有一个指向“state”类/结构的共享指针来存储不可复制的对象(它本身是不可复制的),但你的类可以复制共享指针通过标准的复制构造函数。
答案 1 :(得分:4)
第一个选项以auto_ptr
:
http://www.cplusplus.com/reference/std/memory/auto_ptr/auto_ptr/
auto_ptr
放弃指针并在复制时重置。
确保函数不会更改作为const传递的参数的标准比copy-ctor标准强得多,后者实际上并不是非常正式。
此外,根据标准,通过抛弃其const来操纵const值是未定义的行为。如果您确定引用引用了非const对象,或者您将const对象传递给不会修改该值的(const-incorrect)函数,则可以执行此操作。
答案 2 :(得分:3)
你的课基本上做的是std :; auto_ptr。 auto_ptr的复制构造函数如下所示:
auto_ptr( auto_ptr & p );
这意味着它不能在某些情况下使用,例如在标准容器中。这可能是最好的做法,但值得注意的是auto_ptr已在C ++ 0x中弃用。
答案 3 :(得分:3)
也许复制构造函数应该只使用DuplicateHandle()
来制作一个真正的副本。
答案 4 :(得分:3)
好吧,如果你真的需要一个拷贝构造函数,那么你必须确保它具有正确的语义。如果您实施任何一种方法,那么您就会遇到麻烦。依赖于您的复制构造函数的代码将不知道您的所有权转移方案,这可能会产生不可预测的结果。 (例如,这就是你不能在STL容器中使用std :: auto_ptr的原因。)
如果您确实不需要复制构造函数,则创建一个复制对象并传输句柄所有权的方法。
如果确实需要复制构造函数,则需要一种方法来共享副本之间的句柄。我建议使用辅助对象来存储句柄和引用计数,这样你就知道何时关闭句柄。我还会重新检查你的假设,即句柄是不可复制的。如果唯一的问题是你必须完全关闭它一次,那么你可以使用DuplicateHandle()函数解决问题。
最后一点说明:我会说编程中的基本规则是从不谎言函数的作用。
答案 5 :(得分:2)
也许,不应该为非可复制的对象使用非标准或奇怪的复制构造函数,而应考虑不复制它。您可以使用新的shared_ptr机制或unique_ptr进行所有权切换,而不是自己担心这些内容。
答案 6 :(得分:2)
使用非const
复制构造函数是可以的。我不知道为什么你称之为“非标准”。该标准明确指出(§12.8/ 2)复制构造函数参数不必是const
。它通常是,因为修改其他对象很少是必要的,并且有其缺点(例如,不能在标准容器中使用)。
但我宁愿重新审视设计:你真的需要复制一个明显不可复制的对象吗?语义说不然。或者,你不能以安全的方式复制底层句柄吗?
答案 7 :(得分:1)
我肯定会不实现你在这里的拷贝构造函数。 Detach方法声明为const但实际上并非如此。最好使复制构造函数遵循标准形式,但将其设为私有和/或使其抛出调用。然后,您可以提供克隆方法,该方法复制句柄和任何其他适用的属性。
答案 8 :(得分:1)
这很容易:非标准的。修改其const参数的构造函数很难由开发人员预测。您需要将其记录下来,并且开发人员需要知道查看文档以获得非常常见的用途......这是最不可能的。
非标准构造函数虽然引起了人们的注意。
此外,这是标准如何使用auto_ptr。为什么?所以你不能不小心把它放在一个容器里。使用非标准版本的另一个好理由。
答案 9 :(得分:0)
关于你的编辑问题:这不是你传递的非const对象,而是你试图复制非const 临时,这是不允许的通过非const引用传递。你可以声明一个实际的对象,并且应该纠正它。
Fuzz bar() const { Fuzz fuzz; return fuzz; };
答案 10 :(得分:0)
最简单的解决方案是将HANDLE h
替换为boost::shared_ptr<HANDLE> ph
。现在添加一个私人便利函数HANDLE h() { return *ph; }
。因此,默认复制ctor将做正确的事情。
当然,如果两个对象同时尝试使用句柄,则可能会遇到一些问题。但是,由于您的原始设计在没有手柄的情况下离开了原始对象,因此无论如何情况似乎并非如此。