Compiler: How is class instantiation code compiled? - c++

Assume we have (in C++): MyClass* x = new MyClass(10)
Can someone please explain what 'exactly' happens when compiler parses this statement? (I tried taking a look at the Red Dragon book but couldn't find anything useful).
I want to know what happens in the stack/heap or compiler's symbol table. How compiler keeps track of the type of x variable? How later calls to x->method1(1,2) will be resolved to appropriate methods in MyClass (for simplicity assume there is no inheritance and MyClass is the only class that we have).

MyClass* x is a definition of pointer to object (instance) of type MyClass. Memory for that variable is allocated according to the place of its definition: if it is defined in the method, and is a local variable, stack is used. And it is memory to store the address.
Then expression new MyClass(10) is a command to allocate memory in heap for an object (instance) itself and return address to be stored in x. To fill the memory of new object MyClass (set up its initial state) special method (at least one) is executed automatically - constructor (or several in some cases) - that receives value 10 in your example.
Because C++ allows inheritance (this is also the reason for the execution of several constructors when an instance created) there are some mechanism to determine which method should be exactly called. You should read somewhere about Virtual method table.
In the simplest case (without inheritance), type of variable x (pointer to object of type MyClass) provide all necessary information about object structure. So, x->method1(1,2) or (*x).method1(1,2) provide call of member method1 to execute it with parameters 1 and 2 (stored in stack) as well as with data that form the state of object (stored in heap) and available by this pointer inside any non-static member of class. The method itself, of course, not stored in the heap.
UPDATE:
You can make example to make same experiments, like:
#include <iostream>
#include <string>
using namespace std;
class MyClass
{
private:
int innerData;
long long int lastValue;
public:
MyClass() // default constructor
{
cout << "default constructor" << endl;
innerData = 42;
lastValue = 0;
}
MyClass(int data) // constructor with parameter
{
cout << "constructor with parameter" << endl;
innerData = data;
lastValue = 0;
}
int method1(int factor, int coefficient)
{
cout << "Address in this-pinter " << this << endl;
cout << "Address of innerData " << &innerData << endl;
cout << "Address of lastValue " << &lastValue << endl;
cout << "Address of factor " << &factor << endl;
lastValue = factor * innerData + coefficient;
return lastValue;
}
};
int main(void)
{
MyClass* x = new MyClass(10);
cout << "addres of object " << x << endl;
cout << "addres of pointer " << &x << endl;
cout << "size of object " << sizeof(MyClass) << endl;
cout << "size of pointer " << sizeof(x) << endl;
x->method1(1, 2);
}

C++ is indeed a bit nasty, and this already started in C. Just look at the first 3 tokens : MyClass * x. The compiler has to look up MyClass to determine that this is not a multiplication. Since it is a type, it shouldn't look up x either, but add x to the symbol table right there (really can't be delayed). In the ideal world of simple-to-parse languages, there would be no need to keep the symbol table up to date for every token parsed.
After the definition of x, the = signals an initializing expression. That's an easy one to parse: new is unambiguously a keyword, it's not a placement new and the type being created is right there.

Related

C++ 0/1-argument constructors not chaining as expected when passed to template constructor [duplicate]

This question already has answers here:
Default constructor with empty brackets
(9 answers)
Closed 2 years ago.
In the below code example, I have a class with a templatized 1-argument constructor, called Acceptor1. I want to pass in objects of type Foo, Bar, or Baz to its constructor, chaining the constructor calls. (The reality is that this simplified example reflects something I am trying to do with type erasure: where I want to pass in an object to the eraser class.) What actually happens is that when I try to declare an Acceptor1 object and pass it e.g. a Foo object, I define a variable that points to a function that accepts a FUNCTION pointer (not an Foo object) and returns an Acceptor1. The type of the function pointer is Foo(*)(). The overall variable is declared of type Acceptor1 (Foo (*)()).
I can't work around this with any possible enable_if statement, as shown by the Accessor2 class example, where I didn't even define a 1-argument constructor.
Like I mentioned, the end-use of this is to be able to have a type eraser class that has a constructor looking like Acceptor1, plus the extra mechanics for type erasure. I've done this and it works fine as long as the type I'm erasing isn't constructed with a 0/1-argument constructor. Or, I can declare and assign the variables in two separate statements. However, I have a lot of them, and this doubles the size of the code, plus takes up a few more CPU cycles, I guess.
Does anyone have any ideas on how to get Acceptor1 test( Foo() ) to be treated as defining an Accessor1, and initializing it with a Foo object? It works fine with Accessor1 test( Baz(d, d2) ), a two-argument situation. It also works fine with literals, e.g. Acceptor1 test( Bar(1.0) ). Thanks!
Sean
Here's the code:
#include <iostream>
#include <typeinfo>
using namespace std;
struct Foo {
Foo() {
cout << "Foo" << endl;
}
};
struct Bar {
Bar(double a) {
cout << "Bar" << endl;
}
};
struct Baz {
Baz(double a, double b) {
cout << "Baz" << endl;
}
};
struct Acceptor1 {
template<typename T> //no possible enable_if could help this problem
Acceptor1(T f) {
cout << typeid(T).name() << endl;
}
};
struct Acceptor2 {
};
int main()
{
double d, d2;
std::cout << std::endl;
//0 arguments - captures a conversion constructor
Acceptor1 one(Foo()); //F9Acceptor1PF3FoovEE=Acceptor1 (Foo (*)())
cout << "one: " << typeid(one).name() << endl << endl;
//0 arguments - the result we expect
Acceptor1 two = Acceptor1(Foo()); //9Acceptor1=Acceptor1
cout << "two: " << typeid(two).name() << endl << endl;
//There's no possible way to enable_if it out - I deleted the whole constructor
Acceptor2 three(Foo()); //F9Acceptor2PF3FoovEE=Acceptor2 (Foo (*)())
cout << "three: " << typeid(three).name() << endl << endl;
//1 arguments - captures a conversion constructor
Acceptor1 four(Bar(d)); //F9Acceptor13BarE=Acceptor1 (Bar)
cout << "four: " << typeid(four).name() << endl << endl;
//1 arguments - the result we expect
Acceptor1 five = Acceptor1(Bar(d)); //9Acceptor1=Acceptor1
cout << "five: " << typeid(five).name() << endl << endl;
//There's no possible way to enable_if it out - I deleted the whole constructor
Acceptor2 six(Bar(d)); //F9Acceptor23BarE=Acceptor2 (Bar)
cout << "six: " << typeid(six).name() << endl << endl;
//1 arguments - literal
Acceptor1 seven(Bar(5.0)); //9Acceptor1=Acceptor1
cout << "seven: " << typeid(seven).name() << endl << endl;
//2 arguments - the result we expect
Acceptor1 eight(Baz(d, d2)); //9Acceptor1=Acceptor1
cout << "eight: " << typeid(eight).name() << endl << endl;
//2 arguments - the result we expect
Acceptor1 nine = Acceptor1(Baz(d, d2)); //9Acceptor1=Acceptor1
cout << "nine: " << typeid(nine).name() << endl << endl;
using FooMaker = Foo(&)();
using AcceptorFnToBazMaker = Acceptor1(*)(FooMaker); //PF9Acceptor1RF3FoovEE=Acceptor1 (*)(Foo (&)())
cout << "AcceptorFnToBazMaker: " << typeid(AcceptorFnToBazMaker).name() << endl << endl;
return 0;
}
EDIT: It was suggested that this is a duplicate of Default constructor with empty brackets - I agree that they both mention the "most vexing parse" as the source of the problem, but the questions are different. The answer there even includes an example with a function of 2 parameters. In my case, with constructors, only 0/1-argument constructors are treated specially.
Change Acceptor1 test( Foo() ) to Acceptor1 test((Foo())) or an even better way would be Acceptor1 test{Foo()} or perhaps (as you did) Acceptor1 test = Acceptor1(Foo());
The compiler can interpret the line as a valid function declaration and an object initialization. The standard (as far as I remember) says that in such a case, take it as a function declaration. Here's what's in cppreference about this :
In case of ambiguity between a variable declaration using the direct-initialization syntax (1) (with round parentheses) and a function declaration, the compiler always chooses function declaration. This disambiguation rule is sometimes counter-intuitive and has been called the most vexing parse.
And hence you get this.
To make the compiler interpret this as an object initilization, add a parathesis () around to make it an invalid function declaration. Viz-a-viz, all you have to do is to make sure it's not a valid function declaration syntax.

C++ cast pointer to pointer to derived to pointer to pointer to base example

I have a table of pointers to derived classes. The program uses a lot of small classes that use entries in those tables. Periodically, all those small classes have to be exchanged, with purpose that they use data in a different class in the table, but they only use the base class data. So those small classes use pointers to entries of the table, but those pointers are actually pointers to pointers to the base class. In isolation, it's this, and the below works in gcc 4.8.2 (the real thing has complex classes; this is just an example)
#include <iostream>
class Base {public: int i;};
class Derived : public Base {public: int j;};
int main() {
Base **b;
Derived *table[2];
table[0] = new Derived; table[0]->i = 1; table[0]->j = -2;
table[1] = new Derived; table[1]->i = 4; table[1]->j = -5;
b = reinterpret_cast<Base**>(&(table[0]));
std::cout << "first: " << (**b).i << " " << "\n";
b = reinterpret_cast<Base**>(&(table[1]));
std::cout << "second: " << (**b).i << " " << "\n";
{Derived *temp; temp = table[0]; table[0] = table[1]; table[1] = temp;}
b = reinterpret_cast<Base**>(&(table[0]));
std::cout << "first switched: " << (**b).i << " " << "\n";
b = reinterpret_cast<Base**>(&(table[1]));
std::cout << "second switched: " << (**b).i << " " << "\n";
}
There is no way that the table can ever get corrupted: it always will contain pointers all to exactly the same derived class type, so in theory they can all be exchanged. The classes that use the table will only ever use base class data. If reinterpret_cast is replaced by either dynamic_cast or static_cast then the compiler complains. Now, this is all fine, except for admonitions like this: "...which on most cases results in code which is system-specific, and thus non-portable", and other really scary and severe statements by opinionated experts who obviously know a lot.
Is the above a reasonable and safe use of reinterpret_cast?
Neither safe nor reasonable.
First a bit of a rant. reinterpret_cast is a broadswoard. A weapon of last resort. It is not safe. It is not friendly. It will not protect you from yourself. It should not be your go-to tool of choice. You should not just bang reinterpret_cast in there if static_cast doesn't "work" and you don't understand what the compiler is telling you. Instead, you should first try to understand the compiler's error messages, and then fix the actual problem.
End of rant. Now in your case:
b = reinterpret_cast<Base**>(&(table[0]));
table is an array of pointer-to-Derived. So you are trying to cast from a pointer-to-pointer-to-Derived to a pointer-to-pointer-to-Base. What you should do instead is cast a pointer-to-Derived to pointer-to-Base, like this:
Base* b = static_cast<Base*>(table[0]);
And then use it:
std::cout << "first: " << (*b).i << " " << "\n";
But since Derived is a direct descendant of Base, you don't even need an explicit cast. This would work just fine:
Base* b = table [0];
Even better still, since you don't really need a pointer, why not just grab a reference instead?
Base& b = *table[0];
std::cout << "first: " << b.i << " " << "\n";

C++ Class Constructor / Destructor

I have a below code. Every time Constructor is called, I increase a counter and the counter is decreased every time Destructor is called. After instantiating three class objects, I tried printing out the counter value. Then I tried printing out the counter value again after deleting one of the objects. The expected values were 4 and 3, but instead I get 2 and 1.
I actually tried printing out something within the Constructor and Destructor to observe how many times they were actually called, but surprisingly Destructor was called several times in addition to the time when I called "delete object". Is it because the Destructor is called automatically? If so, is there any way to turn the feature off to test my code?
** The code originally has Add and Mult functions in the class, but I omitted here because the details of the functions seem irrelevant here.
#include <iostream>
using namespace std;
class Complex{
private:
double x, y;
static int count;
Complex Add(Complex como)
{
Complex t;
t.x=x+como.x;
t.y=y+como.y;
return t;
}
Complex Mul(Complex como)
{
Complex t;
t.x=(x*como.x)-(y*como.y);
t.y=(y*como.x)+(x*como.y);
return t;
}
public:
Complex(double a=0, double b=0) : x(a), y(b) {count++;}
~Complex() {count--;}
void Print() {cout << "(" << x << ", " << y << ")" << endl;}
static int GetCount() {return count;}
};
int Complex::count=0;
int main()
{
Complex com1(1.0, 2.0), com2(3.0, 4.0);
Complex com3;
com1.Print(); cout << endl;
com2.Print(); cout << endl;
com3 = com1.Add(com2); com3.Print(); cout << endl;
Complex *pcom4 = new Complex;
*pcom4 = com1.Mul(com2); pcom4->Print(); cout << endl;
cout << "#complex numbers = " << com1.GetCount() << endl;
delete pcom4;
cout << "#complex numbers = " << com1.GetCount() << endl;
return 0;
}
In C++ you can construct objects in three ways:
using the "constructor"
using the "copy constructor"
using the "move constructor"
If don't define them the compiler will automatically write the code for you (unless you stop it from doing that explicitly).
Your method Mul and Add are accepting the other complex number by value and this means that a copy constructor call will be used to copy the argument of the call.
The automatic synthesized copy constructor doesn't increment the counter.
Your methods are taking Complex objects as parameters (not references to existing objects), so new objects are being created for each call and are destroyed at the end of the call.

C++: Creating a shared object rather than a shared pointer to an object

boost::shared_ptr really bothers me. Certainly, I understand the utility of such a thing, but I wish that I could use the shared_ptr<A> as an A*. Consider the following code
class A
{
public:
A() {}
A(int x) {mX = x;}
virtual void setX(int x) {mX = x;}
virtual int getX() const {return mX;}
private:
int mX;
};
class HelpfulContainer
{
public:
//Don't worry, I'll manager the memory from here.
void eventHorizon(A*& a)
{
cout << "It's too late to save it now!" << endl;
delete a;
a = NULL;
}
};
int main()
{
HelpfulContainer helpfulContainer;
A* a1 = new A(1);
A* a2 = new A(*a1);
cout << "*a1 = " << *a1 << endl;
cout << "*a2 = " << *a2 << endl;
a2->setX(2);
cout << "*a1 = " << *a1 << endl;
cout << "*a2 = " << *a2 << endl;
cout << "Demonstrated here a2 is not connected to a1." << endl;
//hey, I wonder what this event horizon function is.
helpfulContainer.eventHorizon(a1);
cout << "*a1 = " << *a1 << endl;//Bad things happen when running this line.
}
Whoever created the HelpfulContainer wasn't thinking about others wanting to retain pointers to A objects. We can't give HelpfulClass boost::shared_ptr objects. But one thing we could do is use the pimlp idiom to create a SharedA which itself is an A:
class SharedA : public A
{
public:
SharedA(A* a) : mImpl(a){}
virtual void setX(int x) {mImpl->setX(x);}
virtual int getX() const {return mImpl->getX();}
private:
boost::shared_ptr<A> mImpl;
};
And then the main function can look something like this:
int main()
{
HelpfulContainer helpfulContainer;
A* sa1 = new SharedA(new A(1));
A* sa2 = new SharedA(sa1);
cout << "*sa1 = " << *sa1 << endl;
cout << "*sa2 = " << *sa2 << endl;
sa2->setX(2);
cout << "*sa1 = " << *sa1 << endl;
cout << "*sa2 = " << *sa2 << endl;
cout << "this demonstrates that sa2 is a shared version of sa1" << endl;
helpfulContainer.eventHorizon(sa1);
sa2->setX(3);
//cout << "*sa1 = " << *sa1 << endl;//Bad things would happen here
cout << "*sa2 = " << *sa2 << endl;
//but this line indicates that the originally created A is still safe and intact.
//only when we call sa2 goes out of scope will the A be deleted.
}
So, my question is this: Is the above pattern a good pattern, or is there something I'm not considering yet. My current project inherited a HelpfulContainer class like above that's deleting the pointers that I need, but I still need the data structure present in the HelpfulContainer.
Update: This question is a follow-on question.
The whole point of shared_ptr is that it (and its copies) own the object that it points to. If you want to give an A to a container that manages its lifetime then you shouldn't be using a shared_ptr at all as it doesn't meet your needs; HelpfulContainer only knows how to be the sole owner of a dynamically created object so you need to give it a pointer to an object that isn't owned by anything else.
I think that it is usually poor design for an object to care about its own lifetime (there are exceptions). It is usually more useful if an object can do a job and something else manages its creation and descruction, choosing the simplest lifetime strategy possible (e.g. local/automatic variable).
If you absolutely have to share ownership between two things that don't co-operate (such as shared_ptr and HelpfulContainer) then you will have to use some sort of proxy technique.
In this case, though, it just looks like HelpfulContainer just isn't that helpful for your situation.
I'm not sure what this does for you.
If helpfulContainer.eventHorizon() always deletes its parameter, then why not just pass a new copy of (the original) A class:
helpfulContainer.eventHorizon(new A(sa1));
Or, if helpfulContainer.eventHorizon() only sometimes deletes its parameter, then making the call as
helpfulContainer.eventHorizon(new SharedA(sa1));
will leak both the SharedA and the original A (sa1) on those occasions when it chooses not to delete.
So you are creating a Stand-In (SharedA) for which deletion is okay. Even though this is kinda awkward, I guess it's necessary to work with your legacy API. To slightly improve this: Allow construction of SharedA from a shared_ptr, but not the other way around - and then only use the SharedP when you absolutely must:
int main()
{
HelpfulContainer helpfulContainer;
boost::shared_ptr<A> sa1(new A(1));
// deletes its parameter, but that's okay
helpfulContainer.eventHorizon(new SharedA(sa1));
}
Implicit conversions to the underlying pointer type are inconsistent with the intended use of shared_ptr in that you can extremely easily pass the shared_ptr to a function etc without realizing it.
It sounds to me like HelpfulContainer is anything BUT helpful and should be fixed or ditched.
If that's not possible then probably the best way is to just copy the A you want to pass in and pass the copy to the container.

Does calling the constructor of an empty class actually use any memory?

Suppose I have a class like
class Empty{
Empty(int a){ cout << a; }
}
And then I invoke it using
int main(){
Empty(2);
return 0;
}
Will this cause any memory to be allocated on the stack for the creation of an "Empty" object? Obviously, the arguments need to be pushed onto the stack, but I don't want to incur any extra overhead. Basically I am using the constructor as a static member.
The reason I want to do this is because of templates. The actual code looks like
template <int which>
class FuncName{
template <class T>
FuncName(const T &value){
if(which == 1){
// specific behavior
}else if(which == 2){
// other specific behavior
}
}
};
which allows me to write something like
int main(){
int a = 1;
FuncName<1>(a);
}
so that I get to specialize one template parameter, while not having to specify the type of T. Also, I am hoping the compiler will optimize the other branches away inside the constructor. If anyone knows if this is true or how to check, that would be greatly appreciated. I assumed also that throwing templates into the situation does not change the "empty class" problem from above, is that right?
Quoting Stroustrup:
Why is the size of an empty class not zero?
To ensure that the addresses of two different objects will be different. For the same reason, "new" always returns pointers to distinct objects. Consider:
class Empty { };
void f()
{
Empty a, b;
if (&a == &b) cout << "impossible: report error to compiler supplier";
Empty* p1 = new Empty;
Empty* p2 = new Empty;
if (p1 == p2) cout << "impossible: report error to compiler supplier";
}
There is an interesting rule that says that an empty base class need not be represented by a separate byte:
struct X : Empty {
int a;
// ...
};
void f(X* p)
{
void* p1 = p;
void* p2 = &p->a;
if (p1 == p2) cout << "nice: good optimizer";
}
This optimization is safe and can be most useful. It allows a programmer to use empty classes to represent very simple concepts without overhead. Some current compilers provide this "empty base class optimization".
It might, it might, not, depending on circumstances. If you say:
Empty e;
Empty * ep = & e;
then obviously things have to be allocated.
Try it and see. Many compilers will eliminate such temporary objects when asked to optimise their output.
If the disassembly is too complex, then create two functions with different numbers of such objects and see if there is any difference in the stack locations of objects surrounding them, something like:
void empty1 ( int x )
{
using namespace std;
int a;
Empty e1 ( x );
int b;
cout << endl;
cout << "empty1" << endl;
cout << hex << int ( &x ) << " " << dec << ( &x - &a ) << endl;
cout << hex << int ( &a ) << " " << dec << ( &a - &b ) << endl;
}
and then try running that compared with an empty8 function with eight Empties created. With g++ on x86, if you do take the address of any of the empties you get a location between x and a on the stack, hence including x in the output. You can't assume that the storage for objects will end up in the same order as they are declared in the source code.