c++ inheritance question - c++

I have a question about this:
class A
{
int a;
int* pa;
public:
A(int i):a(i) , pa(new int(a))
{
cout<<"A ctor"<<a<<endl;
}
~A()
{
delete pa;
cout<<"dtor\n";
}
int * &get()
{
return pa;
}
};
class B : public A
{
int b;
public:
B (A obj): A(obj) , b(0)
{
cout<<"B ctor\n";
}
~B()
{
cout<<"B dtor\n";
}
};
int main()
{
int i = 23 ;
A* p = new B(i);
}
Can tell me why the last line in main compiles? I pass an int into B's constructor which expects an A object instead. I believe that the int is translated to an A in B's constructor, but why?
Thanks in advance.
Avri.

Since you have not declared A constructor as explicit compiler is creating an anomymous instance of A using i and using it to initialize B instance. If you don't want the compiler to do these implicit conversions declare your costructor as explicit. Then you will get a compiler error.

Because A has a single parameter constructor which takes an int and isn't marked explicit you can implicitly convert an int to an A.
When you do new B(i), because the only viable constructor for B takes an A, an attempt is made to convert i to an A and construct the new B from that. This conversion is done by creating a temporary A using the constructor that takes an int.
When the B object is constructed, the base class A is copy constructed from the temporary A which means copying the member variables a and pa from the temporary A.
Strictly, because the constructor takes an A object by value, the temporary is, conceptually, copied again. The compiler may, however, eliminate the temporary by constructing the constructor parameter for B directly from i so the effect may well look like just a single copy.
This will cause a serious error because when the temporary A is destroyed, delete pa will cause the dynamically allocated int to be destroyed but the base class A of the newly allocated B object will still have a copy of this pointer which now no longer points at an invalid object. If the compiler doesn't eliminate one of the copies, a "double free" will happen immediately.
The key aspect of A is that it has a user-defined destructor that performs a resource action (deallocation). This is a strong warning that A needs a user-defined copy constructor and copy assignment operator because compiler generated version are likely not to work consistently with the design of A.
This is known as the "rule of three" which says that if you need a user-defined version of one of the destructor, copy constructor or copy assignment operator then you are likely to need user-defined versions of all of them.
Were you to attempt to free the dynamically allocated B object in your example, it would likely cause a "double free" error. In addition, A's destructor would need to be marked as virtual for a delete through a pointer to A to work correctly.

Since there is a conversion from int to A, implicitly your code is translated into
A* p = new B(A(i));

Related

C++ another unique_ptr incomplete type question

Have looked at various similar questions here but still can't figure out why the following code does not compile:
// these three are defined somewhere
class A;
std::unique_ptr<A> make_a();
void take_a(std::unique_ptr<A>&&);
int main(){
take_a(make_a()); // this fails
return 0;
}
According to this:
If the default deleter is used, T must be complete at the point in
code where the deleter is invoked, which happens in the destructor,
move assignment operator, and reset member function of
std::unique_ptr.
As far as I understand, none of these (destructor, move assignment operator, nor reset member function) happens in main.
So why does compiler needs the definition of A here?
Since main has a unique_ptr within its scope, realistically it would need to know how to delete the object it holds.
It's possible that take_a doesn't actually take ownership of the object, thus main would need to delete.
main gets a temporary unique_ptr from make_a(), let's call it X. It then passes an rvalue reference to X to take_a. It still has the destroy X. Hence, it has to call the destructor after take_a, even though X would typically be empty at that point.
We need class A description (at least constructor and destructor) in order to create and move std::unique_ptr<A> objects; take_a(make_a()) is creating such object because make_a() is pass-by-value. It first creates a new unique_ptr<A> object, initialize it (using default constructor), and then update its value and returns this new object. Here the default constructor/destructor is being used, which the compiler is unable to find.
#include <bits/stdc++.h>
class A {
public: A() {}
public: ~A() {}
};
std::unique_ptr<A> make_a() {
std::cout << "make";
std::unique_ptr <A> tt = nullptr;
return tt;
}
void take_a(std::unique_ptr<A>&&) {
std::cout << "take";
}
int main(){
take_a(make_a()); // this works now
return 0;
}
Edit: Forgot to add the link. Works the other way too.

move class instance holding a unique_ptr

I have a vector of class instances. Each of those instances has a unique_ptr to another class.
Since I never try to copy the class instance or even share the pointer, I felt like unique_ptr are more appropriate than shared_ptrs since the pointer is not shared, but only accessible through the class instance.
Is it bad practice? And why wouldn't this work? I understand that copying an instance to a unique pointer would be ill-formed, but since I move it, I do not understand why this would not be allowed?
Would I have to create a custom move constructor? And what should it do?
The unique ptr should be deleted as soon as the object instance is being removed from the vector as there are no references left, right?
Code Example for better understanding:
class A {
private:
int number;
public:
void f() {
std::cout << "The number is: " << number;
}
A(int i) : number{i} {}
~A() = default;
};
class B {
std::unique_ptr<A> good_a;
B() : good_a{ std::make_unique<A>(1) } {}
~B() = default;
};
int main()
{
std::vector<B> all;
B my_b(123);
all.emplace_back(std::move(my_b));
}
This answer focuses on compilation error you seem to be having. Bad or good practice is left for others to chime in.
Your code have several errors there, but the main one seems to be that your custom B( ) constructor inhibits default move constructor. If you add it, your code becomes well-formed.
Here is a full working code for the reference:
#include <memory>
#include <vector>
class A {
private:
int number;
public:
void f();
A(int i) : number{i} {}
~A() = default;
};
struct B {
std::unique_ptr<A> good_a;
B(int k) : good_a{ std::make_unique<A>(k) } {}
B(B&& ) = default;
B& operator=(B&& ) = default; // not needed for the example, but good for completeness
};
int main()
{
std::vector<B> all;
B my_b(123);
all.emplace_back(std::move(my_b));
}
And why wouldn't this work?
What you described could work.
I do not understand why this would not be allowed?
What you described would be allowed.
Would I have to create a custom move constructor?
No, that wouldn't be necessary, unless you define other special member functions, or have other members (beside the unique pointer) that have deleted or private move constructor.
The unique ptr should be deleted as soon as the object instance is being removed from the vector as there are no references left, right?
Members are destroyed when the super object is destroyed. And the destructor of the unique pointer invokes the deleter on its owned pointer.
Whether there are references to the pointed object has no effect on whether it is deleted or not. Anything referring to the deleted object will be left dangling.
Is it bad practice?
There isn't necessarily anything particularly bad about what you described in general, but that depends on exact details.
One potential issue is that dynamic allocation can be expensive in some cases, and using it unnecessarily would then be unnecessarily expensive. As such, you should to have some reason to allocate the pointed objects dynamically rather than storing them directly as members.
Bugs in your example:
You attempt to initialise B(123) but B has no constructor accepting an integer.
You attempt to initialise a B outside a member function of B, but its constructors and the destructor have private access.
You have user declared destructor for B, but no user declared move constructor or assignment operators and therefore the class isn't movable, which is a requirement for storing in std::vector.
Here is a fixed version that doesn't use unnecessary dynamic allocation:
struct A {
int number;
};
struct B {
A good_a;
};
B my_b{123};
all.push_back(my_b);
Please read this answear.
Depending what is explicitly declared respective constructors with default implementation are implicitly defined or dropped. Rules are described by this table:
Since you have used explicit defined destructor (as default) you have disabled ("not declared") move constructor.
So to fix it you have to explicitly define move constructor or remove definition of destructor: https://godbolt.org/z/dr8KrsTfq

What happens behind the scenes when manually calling a constructor in C++?

This is an educational question, I am interested in what happens behind the scenes when I do:
SomeClass x(arg1, arg2, arg3); // An instance of SomeClass is constructed.
x = SomeClass(arg4, arg5, arg6); // Intent is to create a new instance.
SomeClass does not have operator= implemented.
Does the space allocated to x simply get overwritten as if it was newly allocated memory or what exactly happens? And when is it a good idea?
This can best be explained with the help of a small example:
Live on Coliru
struct A {
A(int a) { cout << "A::ctor\n"; } //ctor
A(const A& a) { cout << "A::copy\n"; } //copy ctor
A& operator=(const A& a) { cout << "A::operator=\n"; } //copy assign
};
int main()
{
A a(2); //calls constructor
a = A(10); //calls constructor first, then copy assignment
}
Output:
A::ctor
A::ctor
A::operator
The above is pretty self explanatory. For the first, only the constructor gets called. For the second, first the constructor is called and then copy assignment.
SomeClass does not have operator= implemented.
That doesn't matter because the compiler can generate one for you. If you explicitly delete it, then the above code will not compile. However, if you have a move constructor defined then that will be used:
(I highly recommend you read The rule of three/five/zero and understand it. It is among the top 5 things in C++ that you should know.)
A& operator=(const A& a) = delete; //copy assign deleted
A& operator=(A&& other) { cout << "move assigned\n"; } //move assign available
Now you maybe wondering what will happen if both copy and move assign are available. Lets see:
A a(2); //ctor
a = A(10); //ctor + move assign
A b(3); //ctor
b = a; // copy assign only
a = std::move(b); // move assign
For a = A(10) move assign is invoked because A(10) is an rvalue of the same type as what is on the left hand side of the =.
For the last case a = std::move(b);, we explicitly cast b to an rvalue (yes that's what std::move() does). Since it's an rvalue now, move assignment is invoked.
Does the space allocated to x simply get overwritten as if it was newly allocated memory or what exactly happens?
First the temporary is created: A(10). Space will of course be allocated for it.
It's result is then assigned to a, so previous values in a get overwritten
destructor for the temporary will be called
And when is it a good idea?
It is a good idea when you need it, it depends on your usecase. Generally I would recommend that don't copy assign unnecessarily.
Second line is call of constructor followed by call to assignment operator. Assigment default to shallow copy of non-static members into existing storage.
If you defined something that prevented compiler to create default operator=, i.e. you defined move constructor or move assignment, no assignment is possible unless you declared your own (why it is so surprising?) If default shallow copy is fine, you can write following declaration:
SomeClass& operator(const SomeClass&) = default;
= default provides mechanism to declare "default" behavior of special functions.
Now there is move assignment and in such case one would be preferred if it declared in given context. But it won't be declared by compiler if user provided destructor or copy\move constructor\assignment operator.
SomeClass& operator(SomeClass&&) = default;
Difference between two assignments exists only for class-types where "move" semantics may include transfer of ownership. For trivial types and primitive types it's a simple copy.
Compiler allowed to elide some actions including creation of storage for temporary object, so resulting code may actually write new values directly into x storage, provided that such elision won't change program behavior.

Returning a different class pointer by overloaded operator new in a class

Suppose i have this sample code:
class A
{
public:
static void* operator new(size_t sz);
private:
int xA;
float yA;
};
class B : public A
{
private:
int xB;
float yB;
};
void* A::operator new(size_t sz)
{
void* ptr = (void*)new B();
return ptr;
}
int main()
{
B* b = (B*) new A();
// Use b ..
delete b;
return 0;
}
Here the constructors will be called in that order (tested in VS2012):
A constructor
B constructor
A constructor
The first two constructors calls are because of the new B() in the overloaded operator new function.
But then the A constuctor will be called again on the pointer returned by the function because the overloaded operator new is supposed to return a pointer to free memory (without creating the object), so the constructor is called again.
If i use the pointer b in this example, is this undefined behaviour?
The code you posted has endless recursion, since you call
A::operator new from within A::operator new; class B
inherits the operator new from A.
Beyond that, you lie to the compiler, which results in undefined
behavior. After new A, you have a pointer to an object whose
type is A. You can legally convert its address to a B*, but
all you can do with that B* is convert it back to an A*;
anything else is undefined behavior.
And it's not clear what you're trying to achieve with the new
B in A::operator new. The compiler will consider any memory
returned from an operator new as raw memory; in this case, it
will construct an A object in it, and from then on out, all
you have is an A object. Any attempt to use it as a B
object is undefined behavior. (And of course, if you actually
need to destruct the B created in A::operator new, you can't
because you've overwritten it.
Finally: you don't have to declare operator new as static;
it implicily is, and it's idiomatic not to write the static in
this case. Similarly, when assigning the results of new B to
a void*, the conversion is idiomatic, and it is idiomatic not
to make it explicit. (It's also best to avoid the C style
casts, since they hide too many errors.)
In general , operator new() should not CREATE an object, it should create space for an object. Your code will overwrite a B object with an A object, and then use it as a B object, and yes, that would be "undefined" (probably covered in the docs under "casting an object to a different type that it wasn't originally created as).
This may appear to work in this particular case, but if the constructor of B is more complex (e.g. there are virtual functions in B), it would immediately fail to work correctly.
If you want to allocate memory for an object, you could do:L
void* A::operator new(size_t sz)
{
void* ptr = (void*)::new unsigned char[sz];
return ptr;
}
Now you are not calling two different constructors for the same object!
The contract of operator new is just the memory allocation, the initialization is done later by the new-expression (by calling the constructor) or by program code if you call the operator directly.
What you are trying to do cannot be done, not like that. You could redesign to use a factory member function that would return a pointer to a B object, for example...

Copying similar classes in C++

I have two classes with similar structure.
class A{
int a;
char *b;
float c;
A(char *str) { //allocate mem and assign to b }
};
class B{
int a;
char *b;
float c;
B(char *str) { //allocate mem and assign to b }
B(B & bref) { //properly copies all the data }
};
I want to copy an object of B to the object of A. Are the following conversions fine?
A aobj("aobjstr");
B bobj("bobjstr");
bobj = aobj; //case 1
bobj = B(aobj); //case 2
Will the case 2 work? Will aobj be properly converted and interpreted as B & when B's copy constructor gets called?
EDIT: What about?
B bobj(aobj)
No, you can't implicitly convert between unrelated types without writing conversion constructors or conversion operators. Presumably, your compiler told you this; mine gave errors such as:
error: no match for ‘operator=’ in ‘bobj = aobj’
note: no known conversion for argument 1 from ‘A’ to ‘const B&’
error: no matching function for call to ‘B::B(A&)’
You could allow the conversion by giving B a conversion constructor:
class B {
// ...
B(A const & a) { /* properly copy the data */ }
};
If you can't change the classes, then you'll need a non-member function to do the conversion; but this is probably only possible if the class members are public. In your example, all the members, including the constructors, are private, so the classes can't be used at all. Presumably, that's not the case in your real code.
If you want to live dangerously, you might be able to get away with explicitly reinterpreting an A object as a B, since they are both standard-layout types with the same data members:
// DANGER: this will break if the layout of either class changes
bobj = reinterpret_cast<B const &>(a);
Note that, since your class allocates memory, it presumably needs to deallocate it in its destructor; and to avoid double deletions, you'll also have to correctly implement both a copy constructor and a copy-assignment operator per the Rule of Three.
If that all sounds like too much work, why not use std::string, which takes care of memory management for you?
If you tried to compile it you would find that none of this works. For one thing your constructors are private.
bobj = aobj; //case 1
This tries to call an assignment operator with the signature:
B& B::operator =(A const&)
This does not exist and compilation will fail.
bobj = B(aobj); //case 2
This tries to call A::operator B(), which does not exist and compilation will fail.
Finally:
B bobj(aobj)
This tries to call a constructor with the signature B::B(A const&), which does not exist and compilation will fail.
You say,
I can't change/modify class A and B, those are generated.
Then either the generator needs to be fixed or you're going to have to write your own adaptors.
You can do this by considering the following notes:
1) char* b makes a problem. When you copy aobj to bobj, the value of pointer aobj.b will be copied to bobj.b which means that they both refer to a same memory location and changing one like this: aobj.b[0] = 'Z' will cause bobj.b to change.
You can solve this by changing b from a pointer to a flat array:
//...
char b[MAXLEN];
//...
The better solution to this is to define a constructor (accepting the other type) and overload the assignment (=) operator, at least for class B, and handle the pointer assignment (allocate buffer for the new pointer and copy content to it).
2) UPDATED: A sample syntax is like this:
bobj = B(reinterpret_cast<B&> (aobj));
// or simply:
bobj = reinterpret_cast<B&> (aobj);
3) Note that this is dangerous, not safe, and not recommended. This means that the design of your solution should probably change. For example, A and B may both inherit from a common base class; B inherits from A; or B explicitly defines a constructor and an assignment operator for class A. These are far more recommended.