Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use base class's constructors and assignment operator in C++?

I have a class B with a set of constructors and an assignment operator.

Here it is:

class B {  public:   B();   B(const string& s);   B(const B& b) { (*this) = b; }   B& operator=(const B & b);   private:   virtual void foo();   // and other private member variables and functions }; 

I want to create an inheriting class D that will just override the function foo(), and no other change is required.

But, I want D to have the same set of constructors, including copy constructor and assignment operator as B:

D(const D& d) { (*this) = d; } D& operator=(const D& d); 

Do I have to rewrite all of them in D, or is there a way to use B's constructors and operator? I would especially want to avoid rewriting the assignment operator because it has to access all of B's private member variables.

like image 701
Igor Avatar asked Aug 04 '09 09:08

Igor


People also ask

How do you call a base class constructor?

To call the parameterized constructor of base class inside the parameterized constructor of sub class, we have to mention it explicitly. The parameterized constructor of base class cannot be called in default constructor of sub class, it should be called in the parameterized constructor of sub class.

How do you write an assignment operator?

The assignment operator is used to assign the value, variable and function to another variable. Let's discuss the various types of the assignment operators such as =, +=, -=, /=, *= and %=. Example of the Assignment Operators: A = 5; // use Assignment symbol to assign 5 to the operand A.

How do you define assignment operator?

An assignment operator is the operator used to assign a new value to a variable, property, event or indexer element in C# programming language. Assignment operators can also be used for logical operations such as bitwise logical operations or operations on integral operands and Boolean operands.


2 Answers

You can explicitly call constructors and assignment operators:

class Base { //... public:     Base(const Base&) { /*...*/ }     Base& operator=(const Base&) { /*...*/ } };  class Derived : public Base {     int additional_; public:     Derived(const Derived& d)         : Base(d) // dispatch to base copy constructor         , additional_(d.additional_)     {     }      Derived& operator=(const Derived& d)     {         Base::operator=(d);         additional_ = d.additional_;         return *this;     } }; 

The interesting thing is that this works even if you didn't explicitly define these functions (it then uses the compiler generated functions).

class ImplicitBase {      int value_;      // No operator=() defined };  class Derived : public ImplicitBase {     const char* name_; public:     Derived& operator=(const Derived& d)     {          ImplicitBase::operator=(d); // Call compiler generated operator=          name_ = strdup(d.name_);          return *this;     } };   
like image 86
Motti Avatar answered Oct 10 '22 05:10

Motti


Short Answer: Yes you will need to repeat the work in D

Long answer:

If your derived class 'D' contains no new member variables then the default versions (generated by the compiler should work just fine). The default Copy constructor will call the parent copy constructor and the default assignment operator will call the parent assignment operator.

But if your class 'D' contains resources then you will need to do some work.

I find your copy constructor a bit strange:

B(const B& b){(*this) = b;}  D(const D& d){(*this) = d;} 

Normally copy constructors chain so that they are copy constructed from the base up. Here because you are calling the assignment operator the copy constructor must call the default constructor to default initialize the object from the bottom up first. Then you go down again using the assignment operator. This seems rather inefficient.

Now if you do an assignment you are copying from the bottom up (or top down) but it seems hard for you to do that and provide a strong exception guarantee. If at any point a resource fails to copy and you throw an exception the object will be in an indeterminate state (which is a bad thing).

Normally I have seen it done the other way around.
The assignment operator is defined in terms of the copy constructor and swap. This is because it makes it easier to provide the strong exception guarantee. I don't think you will be able to provide the strong guarantee by doing it this way around (I could be wrong).

class X {     // If your class has no resources then use the default version.     // Dynamically allocated memory is a resource.     // If any members have a constructor that throws then you will need to     // write your owen version of these to make it exception safe.       X(X const& copy)       // Do most of the work here in the initializer list     { /* Do some Work Here */}      X& operator=(X const& copy)     {         X tmp(copy);      // All resource all allocation happens here.                           // If this fails the copy will throw an exception                            // and 'this' object is unaffected by the exception.         swap(tmp);         return *this;     }     // swap is usually trivial to implement     // and you should easily be able to provide the no-throw guarantee.     void swap(X& s) throws()     {         /* Swap all members */     } }; 

Even if you derive a class D from from X this does not affect this pattern.
Admittedly you need to repeat a bit of the work by making explicit calls into the base class, but this is relatively trivial.

class D: public X {      // Note:     // If D contains no members and only a new version of foo()     // Then the default version of these will work fine.      D(D const& copy)       :X(copy)  // Chain X's copy constructor       // Do most of D's work here in the initializer list     { /* More here */}        D& operator=(D const& copy)     {         D tmp(copy);      // All resource all allocation happens here.                           // If this fails the copy will throw an exception                            // and 'this' object is unaffected by the exception.         swap(tmp);         return *this;     }     // swap is usually trivial to implement     // and you should easily be able to provide the no-throw guarantee.     void swap(D& s) throws()     {         X::swap(s); // swap the base class members         /* Swap all D members */     } }; 
like image 21
Martin York Avatar answered Oct 10 '22 06:10

Martin York