Options for choosing a constructor operator and operator assignment -

I recently reviewed copy constructor, assignment operator, copy swap instance seen here: What is the copy and swap idiom? and many other places -

The link above is a great post, but I still had a few questions - A lot of places answer these questions, on stackoverflow and many other sites, but I have not seen much consistency -

1 - If you have try - catch around areas where we allocate new memory for deep copy in copy constructor? (Ive seen it both ways)

2 - Regarding inheritance for both the copy constructor and the assignment operator, when should the base class functions be called and when should these functions be virtual?

3 - Is std::copy best way to duplicate memory in the copy constructor? I saw this with memcpy , and I saw others say memcpy worst thing on earth.


Consider the example below (thanks for all the reviews), this raised additional questions:

4 - Should we check self-appointment? If so, where

5 - a question with a question, but I saw a replacement used as: std::copy(Other.Data,Other.Data + size,Data); it should be: std::copy(Other.Data,Other.Data + (size-1),Data); if swap goes from "First to Last" and the 0th element is Other.Data?

6 - Why does the constructor with comments not work (I needed to resize for mysize)? Suppose this means that regardless of the order that I write to them, the constructor will always call the distribution element first?

7 - Any other comments on my implementation? I know the code is useless, but I'm just trying to illustrate the point.

 class TBar { public: //Swap Function void swap(TBar &One, TBar &Two) { std::swap(One.b,Two.b); std::swap(One.a,Two.a); } int a; int *b; TBar& operator=(TBar Other) { swap(Other,*this); return (*this); } TBar() : a(0), b(new int) {} //We Always Allocate the int TBar(TBar const &Other) : a(Other.a), b(new int) { std::copy(Other.b,Other.b,b); *b = 22; //Just to have something } virtual ~TBar() { delete b;} }; class TSuperFoo : public TBar { public: int* Data; int size; //Swap Function for copy swap void swap (TSuperFoo &One, TSuperFoo &Two) { std::swap(static_cast<TBar&>(One),static_cast<TBar&>(Two)); std::swap(One.Data,Two.Data); std::swap(One.size,Two.size); } //Default Constructor TSuperFoo(int mysize = 5) : TBar(), size(mysize), Data(new int[mysize]) {} //TSuperFoo(int mysize = 5) : TBar(), size(mysize), Data(new int[size]) {} *1 //Copy Constructor TSuperFoo(TSuperFoo const &Other) : TBar(Other), size(Other.size), Data(new int[Other.size]) // I need [Other.size]! not sizw { std::copy(Other.Data,Other.Data + size,Data); // Should this be (size-1) if std::copy is First -> Last? *2 } //Assignment Operator TSuperFoo& operator=(TSuperFoo Other) { swap(Other,(*this)); return (*this); } ~TSuperFoo() { delete[] Data;} }; 
+6
source share
4 answers
  • If you allocate memory, you need to make sure that it is freed in the event of an exception being thrown. You can do this with an explicit try / catch , or you can use a smart pointer such as std::unique_ptr to hold memory, which will then be automatically deleted when the smart pointer is destroyed by unwinding the stack.

  • You very rarely need a virtual assignment operator. Calling the constructor of the base class copy in the member initialization list and the base class assignment operator first in the derived assignment operator, if you perform a phased assignment --- if you make a copy / swap, then you do not need to call the base class assignment in your derived assignment operator, provided that the copy and swap are implemented correctly.

  • std::copy works with objects and correctly calls copy constructors. If you have simple POD objects, then memcpy will work just as well. I would go for std::copy in most cases, although --- it should be optimized for memcpy under the hood anyway for POD, and it avoids potential errors if you add a copy constructor later.

[Updates for an updated question]

  • When copying / swapping, as it is written, there is no need to check the self-assignment and really not do it --- by the time the other assignment operator is entered, a copy is used, and you have no way to find out what the original object is. This means that the task will still make a copy / swap.

  • std::copy takes as input a pair of iterators (first, first + size). This allows you to use empty ranges and matches every range-based algorithm in the standard library.

  • The commented constructor does not work because members are initialized in the order in which they are declared, regardless of the order in the member initializer list. Therefore, Data always initialized first. If the initialization depends on size , then it will get a duff value, since size is not yet initialized. If you change the size and Data declarations, this constructor will work fine. Good compilers will warn you about a member's initialization order that does not match the declaration order.

+4
source

1 - If you have try-catch around areas where we allocate new memory for deep copy in copy constructor?

In general, you should only catch the exception if you can handle it. If you have a way to localize a low memory condition, then catch it; otherwise let it be.

Of course, you should not usually return from the constructor, if the construction failed, this will cause the calling object with an invalid object and not know that it is invalid.

2 - Regarding inheritance for both the copy constructor and the assignment operator, when should the base class functions be called and when should these functions be virtual?

A constructor cannot be virtual, since virtual functions can only be sent by an object, and there is no object before creating an object. Generally, you should not make assignment operators virtual; plug-in and assignable classes are usually considered non-polymorphic "values" of types.

Usually you call the constructor of the base class copy from the initialization list:

 Derived(Derived const & other) : Base(other), <derived members> {} 

and if you use the copy and swap idiom, then your assignment operator will not need to worry about the base class; to be processed by exchange:

 void swap(Derived & a, Derived & b) { using namespace std; swap(static_cast<Base&>(a), static_cast<Base&>(b)); // and swap the derived class members too } Derived & Derived::operator=(Derived other) { swap(*this, other); return *this; } 

3 - Is std::copy best way to duplicate memory in the copy constructor? I saw this with memcopy , and saw others say memcopy worst thing on earth.

It is rather unusual to deal with raw memory; usually your class contains objects, and often objects cannot be correctly copied by simply copying their memory. You copy objects using their copy constructors or assignment operators, and std::copy will use the assignment operator to copy an array of objects (or, more generally, a sequence of objects).

If you really want to, you can use memcpy to copy objects and POD arrays (plain old data); but std::copy less error prone (since you do not need to provide the size of the object), less fragile (because it will not be interrupted if you change non-POD objects) and potentially faster (because the object size and alignment are known at compile time )

+3
source
  • If the constructor for what you are copying deep can throw something you can handle it, go and catch it. I will just let memory allocation exceptions apply, however.
  • Copy constructors (or any constructors) cannot be virtual. Turn on the base class initializer for them. Assignment copy statements must delegate the base class, even if they are virtual.
  • memcpy() too low for copying class types in C ++ and can lead to undefined behavior. I think std::copy usually the best choice.
+1
source
  • try-catch can be used when you need to undo something. Otherwise, just bad_alloc propagate to the caller.

  • Calling the base class "copy constructor or assignment operator" is the standard way to allow copy processing to be processed. I have never seen a use case for a virtual assignment operator, so I think they are rare.

  • std::copy has the advantage of copying class objects correctly. memcpy pretty limited for what types it can handle.

+1
source

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


All Articles