I have two versions of a C++ code. One give the problem and other does not:
/*
* This compiles fine
*/
class base {
private:
const char c;
};
int main() {
base b(); // compiles fine
}
/*
* This gives compilation error
*/
class base {
private:
const char c;
};
int main() {
base b; // error: structure 'b' with uninitialized const members
}
Note the difference is 'base b()' and 'base b'.
I thought that both will call default constructor and since the class has a const field, the program will fail to compile.
Please help to explain this.
That is because the first version does not create an object of type base, but rather declares a function called b which takes no argument and returns an object of type base:
base b; // Declares an object b of type base
base b(); // Declares a FUNCTION called b that takes no argument an returns a base
In fact, you could try the following to verify that this is indeed the case:
int main() {
base b(); // DECLARES function b()
b(); // INVOKES function b()
}
base b() // DEFINITION of function b()
{
base c;
// ...
return c;
}
Now function main() won't give you problems anymore, but the base c; inside the b() function will. Exactly like the base b; in your original example. Why?
Well, because in general data members whose type is const-qualified should be initialized as soon as you construct the object (just like data members of a reference type). A way to guarantee this in general is to initialize those data members in the constructor's initialization list.
This, for instance, will compile:
class base {
public:
base() : c('x') { }
private:
const char c;
};
int main() {
base b;
}
const char c; Must be defined when declared.
const char c = 'a'; for example
Related
I was looking to create a function that is capable of working with any Derived Object of A.
However in this example, I can't seem to be able to use B Object in a function that has a A Typing on it. Is there any way I pass B into the Function?
class A {
public:
A() {
}
};
class B :A {
public:
B() {
}
};
void function(A a) {
return;
}
int main(void) {
B b();
function(b);
}
I've commented on the fixes needed inline:
class A {
public:
A() {}
};
class B : public A { // public inheritance or A will be an inaccessible base of B
public:
B() {}
};
void function(const A& a) { // take a reference to an A to avoid copy-slicing
// ... work with the A part of the object you reference with `a`
}
int main() { // void not needed (but not an error as such)
B b; // not a function declaration anymore
function(b);
}
Actually you are lucky. You made two mistakes that caused passing b to the function fail, while in fact without that other mistakes you can pass b to the function but it would do the wrong thing silently.
First the two mistakes: B b(); declares a function. To declare a default constructed B you write B b;. Then B inherits privately, hence you cannot convert a B to an A. Thats what the error your code causes have told you.
However, after fixing those (and removing user declared constructors taht shouldnt be there when they do nothing)...
class A {};
class B : public A {};
void function(A a) {}
int main(void) {
B b;
function(b); // object slicing !!
}
This code compiles without errors, but usually it does the wrong thing!
Any B can be converted to an A because the inheritance is public, but what happens is object slicing: What is object slicing?. If B had any members not in A then they would all be lost when passing it to function. Its not an issue here because neither A nor B have any members, but in general you want to avoid objects to get sliced.
TL;DR: References / pointers are needed for polymorphism. Pass by (const) reference:
void function(const A& a) {} // does not modify a
void function(A& a) {} // modifies a
I'm wondering if it's possible to pass a reference to a map where the data is a pointer to a derived class?
#include <map>
class B {
public:
private:
int x_;
};
class D : public B {
public:
private:
int y_;
};
typedef std::map<int, B*> mapB_t;
typedef std::map<int, D*> mapD_t;
void foo(mapB_t& inmap) {
;
}
int main() {
mapB_t mapB;
mapD_t mapD;
mapB[0] = new B;
mapD[0] = new D;
foo(mapB);
foo(mapD);
return 0;
}
I receive this compiler error:
q.cc: In function 'int main()':
q.cc:34: error: invalid initialization of reference of type 'mapB_t&' from expression of type 'mapD_t'
q.cc:22: error: in passing argument 1 of 'void foo(mapB_t&)'
I think the explanation should be that even though polymorphism allows one to pass a D type object to a B& type reference, mapB_t and mapD_t have no inheritance relationship. Therefore mapB_t& does not accept mapD_t. The right way of using polymorphism in your situation might be to create many objects of B and D types, but always define the map to be of type mapB_t. A B* type pointer can point to either B or D. You need to define in class B at least one virtual function to allow the function foo to tell if an object pointed to by the B* pointer is a B or a D. Here's the code:
class B{
private:
int x_;
public:
virtual ~B(){} // Only for telling the identity of an object
};
class D: public B{
private:
int y_; // Virtual destructor gets automatically inherited
};
Then the function foo can tell if an object found by the map is of type B or D using dynamic_cast. Here's the code:
void foo(std::map<int,B*> inmap){
std::map<int,B*>::iterator it=inmap.find([Something]);
D *pd=dynamic_cast<D*>(it->second);
if(pd!=nullptr){
// You know it->second is pointing to an object of type D
}
else{
// Just an ordinary type B
}
}
You'll have a better understanding of polymorphism if you can do this problem: https://www.hackerrank.com/challenges/magic-spells
I am trying to update some older code for Objects (A) with big initialization lists by initializing the member variable for Object (B) in the header.
class A
{
public:
A(); //a lot of members initialized in constructor
A(const A &cpy); //has a defined cctor too
B* member1 = new B("needs a string"); //works fine
B member2 = B("needs a string"); //error: ‘B::B(const B&)’ is private
B member3("needs a string"); //error: expected identifier before string
B member4{(const char*)"needs a string"}; //What I was looking for
}
But I need non pointer members too but can't find a way to initialize them. For various reasons I can not use the copy constructor of B.
Here it follows an example of your code once fixed:
#include <string>
class B {
public:
B(std::string) { }
};
class A {
public:
A() { }
A(const A &cpy) { }
B* member1{new B{"needs a string"}};
B member2{"needs a string"};
};
int main () {
A a;
}
This works, that's the best one can do with such a question.
May an instance of a derived class be implicitly converted to an instance of its base class, when the only candidate constructor in the base class is marked explicit?
I ran this:
struct Base {
Base() {}
explicit Base(Base const& b) {}
};
struct Derived : Base {};
int main() {
Derived d;
Base b = d;
}
And got this:
error: no matching function for call to 'Base::Base(Derived&)'
Then I ran this:
struct Base {
Base() {}
Base(Base const& b) {}
};
struct Derived : Base {};
int main() {
Derived d;
Base b = d;
}
And got no errors.
But I'm not entirely convinced that this test succeeds due to explicitness rather than ctor synthesis. In particular, I didn't think that explicit cared about the type of arguments, but that it would force me to write Base b = static_cast<Base>(d) ... which I'm not doing in either case.
It's not the conversion that fails. Copy-initialization requires an accessible copy-constructor.
This also fails:
struct Base {
Base() {}
explicit Base(Base const& b) {}
};
int main() {
Base d;
Base b = d;
}
In hindsight, it seems clear.
The elements at play here are:
The only would-be-synthesised candidate constructor is Base(Base const&), and I don't provide a constructor Base(Derived const&).
Said constructor is explicit, but I provide no explicit conversion.
So, the answer is "no".
This:
int main() {
Derived d;
Base b = d;
}
is not upcasting. That is creating a new object, called b, which contains a copy of the value of d. In order to have upcasting, you must be using polymorphic values (references or pointers). Upcasting would therefore be:
int main() {
Derived d;
Base &b = d;
}
The variable b contains a reference to the Base part of d. If Base had some public member int baseValue;, then b.baseValue refers to the exact same data as d.baseValue. For example:
int main() {
Derived d;
Base &b = d;
d.baseValue = 10;
cout << b.baseValue << endl;
}
This will write 10. If b weren't a reference, but a regular object, it would have copied the (initialized) value from d before d's value was changed. And therefore changing one would not change the other.
The purpose of the explicit keyword is to prevent syntax like Base b = d from working. If you make a constructor explicit, you are saying, "I don't want the compiler to ever implicitly call this copy constructor. If the user is to use it, then they must explicitly say so." If you want implicit conversion of types, then you must say so.
I have the code:
class A{ //base class
public:
virtual std::string getString(){return "class A";}
};
class B: public A{
public:
std::string getString() {return "it is B class";}
};
class C{
public:
C(){
B b;
a = b;
}
std::string test() {return a.getString();}
private:
A a;
};
int main()
{
C c;
std::cout << c.test();
return 0;
}
c.test() says "class A", but how I can call method getString() from class B and not A?
Thanks!
The problem is, your B object gets sliced when assigned to an A object. This is because you assigned by value, not by reference or pointer. Since you declared a like this
A a;
what happens during the assignment a = b is that the actual state of b is copied over into a. However, since a is a value object, only the A part of object b is copied, and its "B-ness" is completely lost!
To avoid this, you need to declare a as a pointer type, as suggested by others (a reference would also work, but then you would need to considerably rewrite your example, since you can't assign to references, only initialize them). If a is a pointer (A*), the assignment a = b makes a point to the object represented by b, which is still a B object, thus you will observe the polymorphic behaviour you expected. However, in this case, you must ensure that b stays alive even after exiting the constructor - otherwise you leave a dangling reference which causes undefined behaviour (read: bad things you don't want to happen) when dereferenced.
Since a pointer example was already shown by #Nawaz, I will give another using a reference:
class C{
public:
C() : a(b) { // references must be initialized in the constructor initializer list
}
std::string test() {return a.getString();}
private:
B b; // moved to class scope to ensure that it stays alive
A& a;
};
You need to implement like this:
class C{
public:
C(){
a = new B;
}
std::string test() {return a->getString();}
private:
A *a;
};
This will call getString() from class B and not A.
What you're trying to do is called "dynamic polymorphism" which is achieved through pointer (or reference) of type base class (which is A), but the pointer points to an object of type derived class (which is B).
Because your member a is not an A*, it is an A instance. Therefore you are just assigning the A part of B to variable a. if you convert a to an A*, you will get the expected result.
You are slicing therefore it will not work. a is an A it is not a B.
To work your class member variable a must be a pointer or a reference.
As a pointer
class C{
public:
C(){
a = new B;
}
std::string test() {return a->getString();}
private:
A *a;
};
As a reference
class C{
public:
C() : a( *(new B) )
{
}
std::string test() {return a.getString();}
private:
A &a;
};
Of course the code I have produced leaks but will work with the virtual function.