I have the following sample code. class bar is derived from the base class foo and allocates memory for ptr_x while read/write access are granted through the base class. This is a toy model for a large code in which read/write functions are the same for all different variants of an object but memory allocation slightly differs in different variants.
#include <iostream>
class foo{
protected:
int *ptr_x;
public:
foo(){
std::cout << " 1) Inside foo constructor: " << ptr_x << std::endl;
}
void read()
{
std::cout << " 2) Inside read function: " << ptr_x << std::endl;
std::cout << " 3) Inside read function: " << *ptr_x << std::endl;
}
void operator=(const int rhs)
{
std::cout << " 4) Inside operator= : " << ptr_x << std::endl;
*ptr_x = rhs;
}
};
class bar: public foo{
public:
bar(int a)
: foo()
{
std::cout << " 5) Inside bar constructor: " << ptr_x << std::endl;
ptr_x = new int;
std::cout << " 6) Inside bar constructor: " << ptr_x << std::endl;
*ptr_x = a;
}
~bar()
{
std::cout << " 7) Inside bar destructor: " << ptr_x << std::endl;
if (ptr_x != NULL) {delete ptr_x; ptr_x = NULL;}
}
};
int main (){
bar a(20);
a.read();
a = 40;
a.read();
return 0;
}
When I run the code, I get:
1) Inside foo constructor: 0
5) Inside bar constructor: 0
6) Inside bar constructor: 0x1d8f010
2) Inside read function: 0x1d8f010
3) Inside read function: 20
1) Inside foo constructor: 0x7f40c11e3b68
5) Inside bar constructor: 0x7f40c11e3b68
6) Inside bar constructor: 0x1d8f030
7) Inside bar destructor: 0x1d8f030
2) Inside read function: 0x1d8f030
3) Inside read function: 0
7) Inside bar destructor: 0x1d8f030
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0000000001d8f030 ***
I have the following questions: 1) Why does not code enter operator= in the base class? 2) Why is there a second call made to the constructor/destructor ? 3) why is there a double free problem?
what am I doing wrong here?
EDIT: Ok double free problem is obvious, but why is there two instances of same memory locations? This somehow has to do with operator= since when I comment it out everything is fine.
Thanks
Reasoning for the observed behavior:
The compiler uses the conversion constructor in derived class,
bar(int a)
for evaluating:
a = 40;
It takes in the integer 40 and creates a bar object using the conversion constructor and then assigns the created bar object to a using the implicit copy assignment operator(=) generated by the compiler for class bar.
This is the reason you see additional calls to bar constructor and that Base class overloaded = is never called. Also, the reason for double free lies in here, You have multiple bar objects which keep pointing to the dynamic memory allocated for ptr_x and when one of the object goes out of scope the destructor is called which deallocates the memory but leaves the other objects member pointer in a dangling state.
How to solve these problems:
You should mark the conversion constructor explicit if you don't want the compiler to use it for implicit conversions like this.
You should follow the Rule of Three for your class bar.
Caveat:
Note that the = operator in the base classes is always hidden by the implicitly generated = operator for the derived class.
For the first question you should have declared
bar (int a) as
explicit bar(int a).
this gives a proper compilation error that class bar should have = operator defined.
Related
Class B expects to receive an instance of shared_ptr<IError>.
Class A implements IError and is passed by value to the constructor of B.
I would like to understand how this scenario is handled. How does the shared_ptr as a template class handle the conversion to IError?
In a simple case where B receives shared_ptr<A> I assume the copy constructor is called and the reference counter is increased. However since IError is pure virtual a normal copy constructor invocation seems not to be case here?
// Example program
#include <iostream>
#include <string>
class IError
{
public:
virtual ~IError(){};
virtual void OnError() = 0;
};
class A : public IError
{
public:
A(){};
void OnError(){std::cout << "Error Occured" << std::endl;}
};
class B
{
public:
B(std::shared_ptr<IError> errorReporter): mErrorReporter(errorReporter){}
void Action(){mErrorReporter->OnError();}
private:
std::shared_ptr<IError> mErrorReporter;
};
int main()
{
auto objA = std::make_shared<A>();
auto objB = std::make_shared<B>(objA);
objB->Action();
}
Debugging time! Let's find out what happens by using the tools we have available as developers.
The memory of the shared_ptr objA looks like this (type &objA in the memory window; it will be replaced by its address):
It has a pointer to the object (000002172badd8e0) and a pointer to the control block.
The control block looks like this (copy and paste the second value into a new memory window):
It has a pointer to the allocator (first 2 columns), the reference count (1) and the weak reference count (0 + offset 1).
After objB has been created, the control block of objA has changed to a reference count of 2:
And the shared_ptr objB looks like this:
It points to the a shared pointer and to the control block.
The shared pointer in objB points to the same object as before (000002172badd8e0), so no copy of the actual object has been made.
The control block of objB indicates that objB only has a reference count of 1:
a normal copy constructor invocation seems not to be case here?
Correct. No copy of the object is made, as we can confirm with a debugger. But a copy of the shared_ptr has been made.
It doesn't.
Copy a shared_ptr doesn't copy it's point-to object, just like normal pointer
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<IError> i = a;
A* a = new A;
IError* i = a; // no copy A
You are right in that the base class IError is abstract, hence it cannot be instantiated, never mind copied.
The code below has been modified from the original to show how each newly created shared_ptr just increments the reference count of the original shared_ptr. So, a shallow copy.
In your code, as well as in the code below, the underlying object to these shared_ptrs is concrete class A, derived from the abstract IError, so it is legal to shallow copy it.
// Example program
#include <iostream>
#include <string>
#include <memory>
class IError
{
public:
virtual ~IError(){};
virtual void OnError() = 0;
};
class A : public IError
{
public:
A(){std::cout << "A created.\n";};
void OnError(){std::cout << "Error Occured" << std::endl;}
};
class B
{
public:
B(std::shared_ptr<IError> errorReporter): mErrorReporter(errorReporter){
std::cout << "B created from A.\n";
}
void Action(){mErrorReporter->OnError();}
private:
std::shared_ptr<IError> mErrorReporter;
};
int main()
{
auto objA = std::make_shared<A>();
std::cout << "I. Reference count for objA: " << objA.use_count() << '\n';
auto objB = std::make_shared<B>(objA);
std::cout << "II. Reference count for objA: " << objA.use_count() << '\n';
// objB->Action();
auto objBB = std::make_shared<B>(*objB);
std::cout << "Created objBB from objB\n";
std::cout << "III. Reference count for objA: " << objA.use_count() << '\n';
std::cout << "Reference count for objB: " << objB.use_count() << '\n';
std::cout << "Reference count for objBB: " << objBB.use_count() << '\n';
// auto objB_from_base = std::make_shared<B>(IError()); // ERROR: IError is an abstract class
}
with output:
A created.
I. Reference count for objA: 1
B created from A.
II. Reference count for objA: 2
Created objBB from objB
III. Reference count for objA: 3
Reference count for objB: 1
Reference count for objBB: 1
By looking at the example:
#include <iostream>
int wow=0;
class Foo{
int cow = 0;
public:
Foo(){
std::cout << "Foo +\n";
cow = 0;
++wow;
}
Foo(int n){
std::cout << "Foo has " << n << "\n";
cow = n;
++wow;
}
~Foo(){
std::cout << cow << " ~ Foo -\n";
}
void print(){
std::cout << cow << " is the foo#\n";
}
};
int main(){
void * bar = ::operator new(sizeof(Foo));
Foo * a = new(bar) Foo;
*a = Foo(10);
std::cout << wow << std::endl;
a->~Foo();
::operator delete(bar);
return 0;
}
and compiling and running it, the console shows:
Foo+
Foo has 10
10 ~ Foo -
2
10 ~ Foo -
My question is, why is the destructor called upon calling the constructor?
Should the first destructor call be 0 ~ Foo - ? Since that is the first Foo
that is overwritten by Foo(10)?
In this assignment statement
*a = Foo(10);
there is created a temporary object of the type Foo that is assigned to the object specified by the expression *a using the default copy assignment operator (neither copy or move constructor is called here). After the assignment the temporary object is deleted. The undeclared variable cow (it seems it is a data member of the class Foo) of the object pointed to by the pointer a now contains the same value 10. And in the end of the program the object pointed to by the pointer a is also deleted.
As a result you will get two messages
10 ~ Foo -
10 ~ Foo -
The first one is generated by the destructor of the temporary object and the second one is generated by the object pointed to by the pointer a.
I need to understand why x(x + 1) happened only after I get out of the constructor.
class A
{
protected:
int x;
public:
A(int x = 5) : x(x + 1)
{
cout << "In A::A x=" << x << endl;
}
operator int() const { return x; }
};
void main()
{
A a1(10);
cout << a1 << endl ;
}
I was thinking I will get:
In A:: An x=11
11
But somehow I've got:
In A:: An x=10
11
You have two variables named x.
Inside the body of the constructor the argument variable will shadow the member variable. Whenever you use x inside the body of the constructor, it will be the argument, not the member.
To use the member variable you need to explicitly fetch it from the object, like this->x.
General tip: Don't use the same name for symbols in nested scopes. Besides solving this problem, it will also make the code easier to read and understand.
Your parameter is hiding the member variable of the same name - your definition is equivalent to
class A
{
protected:
int y;
public:
A(int x = 5) : y(x + 1)
{
cout << "In A::A x=" << x << endl;
}
operator int() const { return y; }
};
When a parameter has the same name as a member, you need to explicitly refer to the member with this->.
(The initialiser list follows its own rules.)
I have the following sample code (a stripped down version of my programme)
Class 'some_class' has a constructor with default parameters. The compiler is able to recognise this constructor as a copy constructor. In the main function, this constructor is called when I order for a copy constructed object called 'b'. But when I construct 'c' from a function result, the compiler calls a compiler generated copy constructor (which copies the bit pattern). I can tell by the value of c.some_data, which should have been set by my own copy constructor to a value of 2.
1) What does the standard say about this?
2) Is my compiler broken?
I use MinGW with no options but a specification of my source file name and a name for the executable. I got my port of the gnu open source compiler from the official MinGW website, I'm using the latest version. Have I found a bug, or is this due to my (mis)understanding of c++?
Thanks in advance
#include <iostream>
#include <string>
class some_class
{
public:
some_class(int p = 0) :
some_data(p)
{
std::cout << "user defined constructor (p = " << p << ")" << std::endl;
}
some_class(const some_class &, int = 0)
{
std::cout << "user defined copy constructor" << std::endl;
some_data = 2;
}
int some_data;
};
extern some_class some_function_returning_some_class_object();
int main(int, char **)
{
std::cout << "creating a, with no parameters" << std::endl;
some_class a;
std::cout << "creating b, a copy of a" << std::endl;
some_class b = a;
std::cout << "creating c, copy constructed from a function result" << std::endl;
some_class c = some_function_returning_some_class_object();
std::cout << "c.some_data = " << c.some_data << std::endl;
}
some_class some_function_returning_some_class_object()
{
some_class a(1);
return a;
}
The output is as follows:
creating a, with no parameters
user defined constructor (p = 0)
creating b, a copy of a
user defined copy constructor
creating c, copy constructed from a function result
user defined constructor (p = 1)
c.some_data = 1
The compiler is not using the compiler-defined default copy constructor. It is presumably using return value optimization to skip the copy altogether.
I run this code for experimenting copy constructor and assignment operator
class AClass {
private:
int a;
public:
AClass (int a_) : a(a_) {
cout << " constructor AClass(int) " << a << endl;
}
AClass(const AClass & x) : a(x.a) {
cout << " copy constructor AClass(const AClass &) " << a << endl;
}
AClass & operator=(const AClass & x) {
a = x.a;
cout << " AClass& operator=(const AClass &) " << a - endl;
return *this;
}
};
AClass g () {
AClass x(8);
return x;
}
int main () {
cout << " before AClass b = g() " << endl;
AClass b = g();
cout << " after" << endl;
cout << " before AClass c(g()) " << endl;
AClass c (g());
cout << " after" << endl;
}
and found that no message appears for the return x;
Why?
Should not the copy constructor or operator= be called?
This is the output:
before AClass b = g()
constructor AClass(int) 8
after
before AClass c(g())
constructor AClass(int) 8
after
The compiler is allowed to elide copying in a case like this. This is called Return Value Optimization.
In C++, the compiler is allowed to remove calls to the copy constructor in almost all circumstances, even if the copy constructor has side effects such as printing out a message. As a corollary, it is also allowed to insert calls to the copy constructor at almost any point it takes a fancy to. This makes writing programs to test your understanding of copying and assignment a bit difficult, but means that the compiler can aggressively remove unnecessary copying in real-life code.
This is known as "return value optimisation". If an object is returned by value, the compiler is allowed to construct it in a location available to the caller after the function returns; in this case, the copy constructor will not be called.
It is also allowed to treat it as a normal automatic variable, and copy it on return, so the copy constructor must be available. Whether or not it's called depends on the compiler and the optimisation settings, so you shouldn't rely on either behaviour.
This is called Copy Ellision. The compiler is allowed to ellide copies in virtually any situation. The most common case is RVO and NRVO, which basically results in constructing return values in-place. I'll demonstrate the transformation.
void g (char* memory) {
new (memory) AClass(8);
}
int main () {
char __hidden__variable[sizeof(AClass)];
g(__hidden__variable);
AClass& b = *(AClass*)&__hidden__variable[0];
cout -- " after" -- endl;
// The same process occurs for c.
}
The code has the same effect, but now only one instance of AClass exists.
The compiler may have optimized away the copy constructor call. Basically, it moves the object.
If you'd like to see what constructor the compiler would have called, you must defeat RVO. Replace your g() function thus:
int i;
AClass g () {
if(i) {
AClass x(8);
return x;
} else {
AClass x(9);
return x;
}
}