Which is better: false copy constructor or non-standard?

I have a C ++ class that contains a non-copyable descriptor. However, the class must have a copy constructor. So, I implemented one that transfers ownership of the descriptor to a new object (as shown below),

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 

My problem is that the standard copy constructor accepts a link constant, and I modify that link. So, I would like to know which is better (and why):

  • custom copy constructor: Foo( Foo& other );

  • constructor instance that "lies": Foo( const Foo& other );


Edit:

DuplicateHandle() only works with certain types of descriptors. This is not one of them. This descriptor cannot be duplicated, copied or cloned.

Several people indicated that I was mistaken in suggesting that this is a non-standard copy constructor, and that std::auto_ptr does this. I think that is probably the way to go. But, I get warnings every time I use the class, when I make a copy of ctor, I take a non-constant value. For instance:

 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 

Can anyone tell me what I should do with them?


Edit2:

Everyone seems to lead me to the std::auto_ptr<> method of doing things. So, I looked there, and it uses an intermediate structure to get around the problem described in the first edit. This is the solution I came across.

 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 

It automatically compiles at level 4 and seems to work. Please let me know if this will be something more irresponsible than my original post.

+4
source share
11 answers

The first option has a well-established use case in the form of auto_ptr :

http://www.cplusplus.com/reference/std/memory/auto_ptr/auto_ptr/

auto_ptr returns its pointer and reset when copying.

The standard that ensures that the function does not change the arguments passed as const is much stronger than the copy-ctor standard, which is really not very formal.

Also, manipulating a const value by discarding its constant is undefined behavior according to the standard. You can do this if you are sure that the link refers to a non-constant object, or you pass a const object to a function (const-wrong) that will not change the value.

+4
source

Both are terrible. If your class has a member variable that cannot be copied, then your class cannot be copied.

If your class cannot be copied, it is unacceptable, one of them is a general pointer to the "state" class / struct for storing objects that cannot be copied (which themselves cannot be copied), but your classes can copy the general pointer around through the standard copy constructor.

+6
source

Your class basically does what std :; does auto_ptr. The copy constructor for auto_ptr looks like this:

 auto_ptr( auto_ptr & p ); 

which means that it cannot be used in certain situations, for example, in standard containers. This is probably best practice, but it is noticeable that auto_ptr is deprecated in C ++ 0x.

+3
source

Perhaps the copy constructor should just use DuplicateHandle() to make a real copy.

+3
source

Well, if you really need a copy constructor, you have to make sure that it has the correct semantics. If you are implementing any of your approaches, then you are asking for problems. Code that depends on your copy constructor will not know about your ownership transfer scheme, and this may have unpredictable results. (This is why, for example, you cannot use std :: auto_ptr in an STL container.)

If you really don't need a copy constructor, create a method that copies the object and transfers ownership.

If you need a copy constructor, you need a way to share the descriptor between the copies. I would suggest using the auxiallary object to store the descriptor and reference count, so you know when to close the descriptor. I also rethought your assumption that the handle is not copied. If the only problem is that you have to close it exactly once, then you can work around the problem using the DuplicateHandle () function.

One final note: I would say that a cardinal rule in programming never lies about what a function does.

+3
source

Perhaps instead of using a custom or wierd copy constructor for an object that is not being copied, you should not copy it. You can use the new shared_ptr or unique_ptr equipment to transfer ownership, instead of worrying about it.

+2
source

Using a copy constructor is not const quite normal. I do not know why you call it "non-standard." The standard clearly states (ยง12.8 / 2) that the copy constructor argument should not be const . This usually happens because changing another object is rarely necessary and has its drawbacks (for example, cannot be used in standard containers).

But I would rather go back to design: do you really need to really copy an object that is clearly not copyable? The semantics say otherwise. Alternatively, can you safely copy the base descriptor?

  • Either just assign it, or use the sharing semantics, perhaps together with a reference counter, so that you don't close the handle twice or too soon.
  • Or cause duplication of the base descriptor object.
+2
source

I would definitely use a non -copy constructor like you are here. The Detach method is declared as const, but in fact it is not. It would be better if the copy constructor conforms to the standard form, but makes it private and / or makes it challenge. You can then provide a Clone method that duplicates the handle and any other applicable attributes.

+1
source

It is easy: non-standard. A developer who changes his const parameter will be very difficult to predict for the developer. You need it to be documented, and the developer needs to know to look at the documentation for very common use ... there won't be anything more.

Non-standard designer, although it draws attention.

Also, as the standard does with auto_ptr. What for? Therefore, you cannot accidentally put it in a container. Another good reason to use a custom version.

+1
source

As for your editing question: this does not mean that you are passing a non-constant object, it means that you are trying to copy a non-constant temporary code that is not allowed to be passed by a non-constant link. You can simply declare the actual object, and it should fix it.

 Fuzz bar() const { Fuzz fuzz; return fuzz; }; 
0
source

The simplest solution is to replace HANDLE h with boost::shared_ptr<HANDLE> ph . Now add the private convenience function HANDLE h() { return *ph; } HANDLE h() { return *ph; } . As a result, a copy of ctor will do the right thing by default.

Of course, you may have problems if both objects try to use a descriptor at the same time. But since your original design left the original object without a pen, this is still not the case.

0
source

Source: https://habr.com/ru/post/1312342/


All Articles