Object oriented programming , inheritance , copy constructors - c++

Suppose I have a base class Person and I publicly inherit a class Teacher from base class Person.
Now in the main function I write something like this
// name will be passed to the base class constructor and 17
// is for derived class constructor.
Teacher object(“name”,17) ;
Teacher object1=object; //call to copy constructor
Now I have not written the copy constructor for both the classes, off course the default copy constructors will be called. The Person class’s default copy constructor will first call the base class’s copy constructor.
Now the problem is suppose I write the copy constructor for the base class only, what happens is, the default copy constructor of the derived class will call my written copy constructor.
Now suppose I write the copy constructor for both the classes . now the copy constructor of the derived class (i.e Teacher) will call the default constructor of the base class but not the copy constructor why?
Is only default copy constructor of the derived class can call the copy constructor of the base class automatically?

You have to call the base copy constructor explicitly:
Teacher(const Teacher& other)
: Person(other) // <--- call Person's copy constructor.
, num_(other.num_)
{
}
Otherwise Person's default constructor will be called.
I seem to not fully understand the question so I'll just say everything I think is relevant and hopefully this will help the OP.
All user defined constructors call their base's default constructor by default (unless they explicitly call a different constructor), it doesn't matter if the base's default constructor is user defined or compiler generated.
When a copy constructor is generated by the compiler it will call the base class's copy constructor.
Compiler defined constructors are not special, they can be called explicitly:
class Base {
int num_
public:
Base(int n) : num_(n) { }
// copy constructor defined by compiler
};
class Derived : public Base {
float flt_;
public:
Derived(float f, int n) : Base(n), flt_(f) { }
// Copy constructor
Derived(const Derived& other)
: Base(other) // OK to explicitly call compiler generated copy constructor
, flt_(other.flt_)
{
}
};
For more details see this Wikipedia article.

If you don't specify a copy constructor the compiler generates one automatically. This constructor is generated in a way that it calls the copy constructor of the base class.
If you implement the copy constructor yourself, you also specify what base class constructor should be used (see Motti's answer). If you don't specify anything, the default constructor is used (That's why it is called "default constructor": it is used when no constructor is explicitly specified).
SO the compiler automatically generates a reasonable copy constructor, but if you want something special no further magic is going on and you have to specify yourself how that constructor exactly should look like.

class Base {
int num_
public:
Base(int n) : num_(n) { }
// copy constructor defined by compiler
};
class Derived : public Base {
float flt_;
public:
Derived(float f, int n) : Base(n), flt_(f) { }
// Copy constructor
Derived(const Derived& other)
: Base(other) // OK to explicitly call compiler generated copy constructor
, flt_(other.flt_)
{
}
};

Related

Why is user defined copy constructor calling base constructor while default copy constructor doesn't? [duplicate]

This question already has answers here:
Why does the implicit copy constructor calls the base class copy constructor and the defined copy constructor doesn't?
(3 answers)
Closed 11 months ago.
Consider the following example:
class A
{
public:
A()
{
cout<<"constructor A called: "<<this<<endl;
};
A(A const& other) = default;
};
class B : public A
{
public:
B()
{
cout<<"constructor B called: "<<this<<endl;
};
//This calls A's constructor
B(B const& other)
{
cout<<"B copy constructor called: "<<this<<endl;
};
//While this doesn't call A's constructor
//B(B const& other) = default;
};
int main()
{
B b;
B b2(b);
cout<<"b: "<<&b<<endl;
cout<<"b2: "<<&b2<<endl;
return 0;
}
Output:
constructor A called: 0x7fffc2fddda8
constructor B called: 0x7fffc2fddda8
constructor A called: 0x7fffc2fdddb0
B copy constructor called: 0x7fffc2fdddb0
b: 0x7fffc2fddda8
b2: 0x7fffc2fdddb0
Why is the constructor of A is called when copying B?
Shouldn't the copy constructor of A be called instead?
However, if you change class B's copy constructor to be default, the constructor of A is not called when copying which makes sense.
It will be nice if someone can give a reasonable explanation as to why.
Why is the constructor of A is called when copying B? Shouldn't the copy constructor of A be called instead?
No, it shouldn't.
A derived class must always initialize a base class. If the derived class has a constructor that is implemented explicitly by the user, but it does not explicitly call a base class constructor in its member initialization list, the compiler will make an implicit call to the base class's default constructor, regardless of the type of the derived constructor. The compiler does not make any assumption about the user's intent in implementing the derived constructor. If the user wants a specific base class constructor to be called, they need to make that call themselves.
Since B has an explicitly implemented copy constructor that lacks a member initialization list, the compiler initializes A by calling its default constructor, not its copy constructor.
IOW, this:
B(B const& other)
{
...
}
Is equivalent to this:
B(B const& other) : A()
{
...
}
NOT to this, as you are thinking:
B(B const& other) : A(other)
{
...
}
However, if you change class B's copy constructor to be default, the constructor of A is not called when copying which makes sense.
Correct, a default'ed copy constructor will call the base class's copy constructor, not its default constructor. The compiler is implicitly implementing the entire derived constructor, and so it will choose the appropriate base class constructor to call.

Move constructor not inherited nor default generated

I tried extending std::ifstream with one function to make it easier to read binary variables, and to my surprise, with using std::ifstream::ifstream; the move constructor is not inherited. Worse yet, it is explicitly deleted.
#include <fstream>
class BinFile: public std::ifstream
{
public:
using std::ifstream::ifstream;
//BinFile(BinFile&&) = default; // <- compilation warning: Explicitly defaulted move constructor is implicitly deleted
template<typename T>
bool read_binary(T* var, std::streamsize nmemb = 1)
{
const std::streamsize count = nmemb * sizeof *var;
read(reinterpret_cast<char*>(var), count);
return gcount() == count;
}
};
auto f()
{
std::ifstream ret("some file"); // Works!
//BinFile ret("some file"); // <- compilation error: Call to implicitly-deleted copy constructor of 'BinFile'
return ret;
}
I don't want to explicitly implement the move constructor because it just feels wrong. Questions:
Why it is deleted?
Does it makes sense for it to be deleted?
Is there a way to fix my class so that the move constructor is properly inherited?
The issue is that basic_istream (a base of basic_ifstream, of which template ifstream is an instantiation) virtually inherits from basic_ios, and basic_ios has a deleted move constructor (in addition to a protected default constructor).
(The reason for virtual inheritance is that there is a diamond in the inheritance tree of fstream, which inherits from ifstream and ofstream.)
It's a little known and/or easily forgotten fact that the most derived class constructor calls its (inherited) virtual base constructors directly, and if it does not do so explicitly in the base-or-member-init-list then the virtual base's default constructor will be called. However (and this is even more obscure), for a copy/move constructor implicitly defined or declared as defaulted, the virtual base class constructor selected is not the default constructor but is the corresponding copy/move constructor; if this is deleted or inaccessible the most derived class copy/move constructor will be defined as deleted.
Here's an example (that works as far back as C++98):
struct B { B(); B(int); private: B(B const&); };
struct C : virtual B { C(C const&) : B(42) {} };
struct D : C {
// D(D const& d) : C(d) {}
};
D f(D const& d) { return d; } // fails
(Here B corresponds to basic_ios, C to ifstream and D to your BinFile; basic_istream is unnecessary for the demonstration.)
If the hand-rolled copy constructor of D is uncommented, the program will compile but it will call B::B(), not B::B(int). This is one reason why it is a bad idea to inherit from classes that have not explicitly given you permission to do so; you may not be calling the same virtual base constructor that would be called by the constructor of the class you are inheriting from if that constructor were called as a most-derived class constructor.
As to what you can do, I believe that a hand-written move constructor should work, since in both libstdc++ and libcxx the move constructor of basic_ifstream does not call a non-default constructor of basic_ios (there is one, from a basic_streambuf pointer), but instead initializes it in the constructor body (it looks like this is what [ifstream.cons]/4 is saying). It would be worth reading Extending the C++ Standard Library by inheritance? for other potential gotchas.
As the previous answer mentioned the constructor is defined as deleted in the base class.
It means, you can't use it - see <istream>:
__CLR_OR_THIS_CALL basic_istream(const basic_istream&) = delete;
basic_istream& __CLR_OR_THIS_CALL operator=(const basic_istream&) = delete;
And the return ret tries to use the deleted copy constructor and not the move constructor.
However if you crate your own move constructor it should work:
BinFile(BinFile&& other) : std::ifstream(std::move(other))
{
}
You can see this in one of the comments of your question (#igor-tandetnik).

deleted constructor inheritance

One of the requirements of constructor inheritance is that the derived class can not have any constructors with the same signature. I am unsure however about how deleted functions behave under these rules.
class Foo
{
public:
Foo() = delete;
Foo(const Foo& a_Foo) = delete;
Foo(int a_Value) : m_Value(a_Value) {}
private:
int m_Value;
};
class Bar : public Foo
{
public:
using Foo::Foo;
Bar() : Foo(7) {};
Bar(const Bar& a_Bar) : Foo(12) {};
};
Are deleted constructors inherited at all?
If so, Bar() and Foo() have the same signature, does this make the code invalid?
You could argue that Foo(const Foo& a_Foo) and Bar(const Bar& a_Bar) have different signatures. How do copy constructors behave under constructor inheritance?
Default, copy, and move constructors are not inherited, nor can inheriting a constructor implicitly declare a copy or move constructor for the derived class. Also, an inheriting constructor declaration will essentially just "skip over" a base class constructor if there's already a constructor with the same signature in the derived class.
For each non-template constructor in the candidate set of inherited constructors other than a constructor
having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly
declared with the same constructor characteristics unless there is a user-declared constructor with the same
signature in the complete class where the using-declaration appears or the constructor would be a default,
copy, or move constructor for that class.
([class.inhctor]/3)
Also, an inherited constructor is deleted if the corresponding base class constructor is deleted.
A constructor so declared has the same access as the corresponding constructor in X. It is deleted if the
corresponding constructor in X is deleted (8.4). An inheriting constructor shall not be explicitly instantiated (14.7.2) or explicitly specialized (14.7.3).
([class.inhctor]/4)
Foo constructor is deleted, so you can't define a constructor of class Bar that uses deleted Foo constructor. It doesn't make sense to talk about inheritance there, because you can never create an object of class Bar that uses deleted Foo constructor.
Are deleted constructors inherited at all?
No. They are deleted, there is nothing to inherit from.
If so, Bar() and Foo() have the same signature, does this make the
code invalid?
Default constructor can't be inherited.
You could argue that Foo(const Foo& a_Foo) and Bar(const Bar& a_Bar)
have different signatures. How do copy constructors behave under
constructor inheritance?
Copy constructors can't be inherited as well.
Consider the following example:
class Symbol {
public:
Symbol() = delete;
Symbol(char ch) : c{ch} {}
private:
char c;
//
};
class Derived : Symbol {
public:
private:
int i;
};
class NotDerived {
public:
private:
int i;
};
The following line would not compile. You cannot create an object of a class with a deleted constructor. Everyone knows that.
// Can't create an object because of deleted constructor.
Symbol sym;
However, you can create the object of the base class using the overloaded constructor:
// Parametrized constructor, this one is fine.
Symbol sym_char('a');
The Derived class, using : public inheritance, inherits everything from the base Symbol class, including the deleted constructor. Indeed, if the Symbol class has a deleted constructor, disallowing you to create objects of that class using the default constructor, then logically the Derived class should also forbid you to create objects of that class using the deleted constructor.
// note: default constructor of 'Derived' is implicitly deleted because base class 'Symbol' has a deleted default constructor
// The deleted consturctor is inherited from the base class.
Derived d;
The Derived class inherits all and any methods from the Symbol class, including any deleted constructors!
If you take away the inheritance statement, observe that the inherited deleted constructor disappears. There is no deleted constructor any more. Now the default compiler generated default constructor comes back, because the deleted constructor isn't overwriting it.
// No deleted constructor: works
NotDerived nd;
The NotDerived class has a compiler generated default constructor, because it is not inheriting from anything. The Derived class does not have a default constructor, it has a deleted constructor, because it inherits from a class that has a deleted constructor.
So as we see in the above experiments, yes all and any methods that are in the base class including deleted ones get inherited into the derived class. This means that yes deleted constructors in base classes do get inherited into derived classes!

how to force base class constructors to be called in derived classes?

basic c++ question i'm fairly sure. if i have a base class with a constructor that takes no parameters, and just initializes some of the protected members, does a derived class instantly call this base constructor too if it matches the parameters (wishful but unlikely thinking), and if not, is there a way to force it to automatically call said base constructor from the derived class WITHOUT having to explicitly tell it to do so in the derived class? I ask because i'm writing a wrapper of sorts and there are some protected members that i want initialized to specific values initially, and then i want to derive and manipulate this base class to my needs, but i wouldn't like an outside user to have to remember to explicitly call the base constructor or set these values within their own constructor.
Yes, the default base constructor is always called unless explicitly stated otherwise.
For example:
class A
{
public:
A() { std::cout << "A"; }
};
class B : A
{
public:
B() {}
};
int main()
{
B b;
return 0;
}
will output:
A
By "explicitly stated otherwise" I mean that you can call a different constructor from the derived class:
class A
{
public:
A() { std::cout << "A"; }
A(int) { std::cout << "AAA"; }
};
class B : A
{
public:
B() : A(1) {} //call A(int)
};
int main()
{
B b;
return 0;
}
will output
AAA
Important if you don't have a default constructor (you declare a non-default constructor and not a default one) or the default constructor is not visible (marked as private), you need to explicitly call an available constructor in the derived class.
If your base-class has a "default constructor" (a constructor that takes no parameters; either explicitly provided by you, or implicitly provided by the compiler because you didn't explicitly provide any constructors), then every derived-class constructor will automatically call that unless you specify that they call a different constructor instead.
(If your base-class doesn't have a "default constructor", because you've provided one or more constructors that take parameters and no constructor that doesn't, then it's a compile-error for a derived-class constructor not to indicate the base-class constructor it calls.)

What is the importance of invoking base class constructor explicitly?

class A {
A() { }
};
class B : public A {
B() : A() { }
};
Why do we need to call the base class's constructor explicitly inside B's constructor? Isn't it implicit?
It is implicit. You'll need this syntax in case A has a constructor that has arguments, this is the way to pass them.
This is implicit and unnecessary. If you do not call the base class constructor explicitly, the default constructor (the one with no parameters) is used.
There is only a need to call a constructor explicitly if the base class does not have a default constructor, or if you want to call a different one than the default constructor.
The explicit call to the base class constructor can still be added for clarity, but it is not required.
This is only needed in case A's constructor requires extra parameters. In your example, it is not necessary to call the base class' constructor explicitly.
In the case you've shown, it's not necessary. Consider the following:
class A
{
private:
int m_i;
public:
A(int i)
: m_i(i)
{
}
};
class B:public A
{
public:
B()
{
}
};
This won't work (will not compile) because there is no longer a default constructor for A. In this case, you have to explicitly call the base class constructor with the appropriate argument.
The other time you may have to explicitly call a constructor is when the base class has multiple constructors. In this case, you can explicitly call the one that makes sense.