I have created simple object containing integer and few methods, integer primitive variable alone and compare their sizes. "sizeof()" for both returned value of "4". Why is that - shouldn't object that is composite type and contains information about methods take more space?
#include <iostream>
class Person{
private:
int a;
public:
void hello(){
std::cout << "hello";
}
void DoSomething(){
a++;
}
};
int main(){
int a;
Person p;
std::cout << sizeof(a) << std::endl;
std::cout << sizeof(p) << std::endl;
return 0;
}
The methods (technically member functions in C++ terminology) do not contribute to the "size" of an object. If you think about it, it makes sense. A member function is applicable to any object instantiated from the class or its descendants, and is in a sense independent of the instance. You can think of the member function of some fictive class Foo as being a standalone function declared as
return_type some_member_function(Foo* ptr, /* other params */){...}
where the first parameter is a pointer to the instance you are applying the function to. The compiler actually passes the pointer ptr implicitly when calling the member function, so
foo.some_member_function(/* other params */)
is being translated internally as
some_member_function(&foo, /* other params */)
You can use the address of the current instance pointed by ptr in actual C++ code via the keyword this. Technically the keyword this is a prvalue expression whose value is the address of the object, on which the member function is being called.
PS: as #greyfade mentioned in the comment, the size of an object may be increased by a mere declaration of virtual member function(s), since in this case the compiler must store internally a pointer to the so called "virtual table". This is the case no matter if the virtual function is being overridden or not. So, if you care about object size, don't blindly make the destructor virtual, unless you plan to use your class as the base in a hierarchy.
Related
The code below works, but I'm not quite sure I understand why the member function pointer memfunc_ptr ends up pointing to the correct function Derived::member_func() (see example here). I know that a member function pointer defines an offset into the class of the object, for which it's defined, in this case class Base. So, to justify the result obtained by the code, i.e., that the member function Derived::member_func is called, instead of Base::member_func, I have to conclude that this offset is applied to the vtable of the class Derived, as member_func() is virtual, and the object d is of class Derived. Does that make sense?
#include <iostream>
class Base {
public:
virtual void member_func() {
std::cout << "Base" << '\n'; };
};
class Derived : public Base {
public:
virtual void member_func() { std::cout << "Derived" << '\n'; };
};
int main() {
typedef void (Base::*MFP)();
MFP memfunc_ptr;
memfunc_ptr = &Base::member_func;
Derived d;
(d.*memfunc_ptr)();
}
member_func() is a virtual function. The compiler will hence always call the most appropriate function for the real type of the object that is pointed to. The way this is ensured is implementation specific.
The most frequently way to do it is to use a vtable, a table of funtion pointers. A pointer to the vtable is initialised during the construction of the object. Every time you refer to such a virtual function, the compiler will generate code to find the function pointer in the vtable (using an ofset in the vtable). And when you use a pointer to a virtual function, the compiler will generate code to find the right pointer in the vtable.
This article will show you how this work with more details.
I read about virtual functions but i am not able to clear the concept.
In the below mentioned example.We are creating a base pointer and assigning base object first and calling function is base class and later assigning derived object and calling its function. Since we have already mentioned which objects will be assigned does not compiler know which object function to call during compilation? I did not get why the decision will be delayed till run time. Am i missing something here.?
#include <iostream>
using std::cout;
using std::endl;
// Virtual function selection
class Base
{
public:
virtual void print() const
{
cout << "Inside Base" << endl;
}
};
class Derived : public Base
{
public:
// virtual as well
void print() const
{
cout << "Inside Derived" << endl;
}
};
int main()
{
Base b;
Derived f;
Base* pb = &b; // points at a Base object
pb->print(); // call Base::print()
pb = &f; // points at Derived object
pb->print(); // call Derived::print()
}
In your particular case, the compiler could potentially figure out the type of the objects being pointer at by the base class pointer. But the virtual dispatch mechanism is designed for situations in which you do not have this information at compile time. For example,
int n;
std::cin >> n;
Base b;
Derived d;
Base* pb = n == 42 ? &b : &d;
Here, the choice is made based on user input. The compiler cannot know what pb will point to.
Since we have already mentioned which objects will be assigned does not compiler know which object function to call during compilation? I did not get why the decision will be delayed till run time.
In this very specific, contrived case, your compiler can optimise out all the polymorphism, yes.
Am i missing something here.?
The imagination to realise that the vast majority of code in real life is not this simple. There are infinitely many C++ programs for which the compiler does not have enough information to perform this optimisation.
As per my understanding, the compiler will just look at the reference type at compile time and bind the function defined and declared in that class. Since the Derived -> print() should be called you have to make the print function virtual in the base class so that the compiler will delay the binding to run time and use the function defined in the derived class.
Due to the fact that it is virtual, it is able to dynamically bind the function to the correct object. This means that the pointer calling the function will call the referenced object's function.
Let's say I have 2 classes Instrument and Brass, where Brass is derived from Instrument:
class Instrument
{
protected:
std::string _sound;
public:
Instrument(std::string sound) : _sound(sound) {}
virtual void play() { std::cout << _sound << std::endl; }
};
class Brass : public Instrument
{
private:
std::string _pitchShifter;
public:
Brass(std::string pitchShifter) : Instrument("braaaaaa"), _pitchShifter(pitchShifter)
{}
void printPitchShifter() { std::cout << _pitchShifter << std::endl; }
}
For some crazy reason I have a pointer to a member function of Instrument:
typedef void(Instrument::*instrumentVoidFunc)() //declaring member function pointers is too damn confusing
instrumentVoidFunc instrumentAction;
Now obviously, this will work, with well-defined behaviour:
Instrument soundMaker("bang!");
instrumentAction = &Instrument::play;
(soundMaker.*instrumentAction)();
And the output should be bang!.
But I can also make instrumentAction point to a Brass member function not present in Instrument by upcasting it, like so:
instrumentAction = static_cast<instrumentVoidFunc>(&Brass::printPitchShifter);
My understanding is (or was, anyway) that upcasting the member function pointer should destroy any ability for it to refer to derived class functions that aren't already present in the base class. However:
Brass trumpet("valves");
(trumpet.*instrumentAction)();
...prints out valves just as if I had called the function on the derived class normally. So apparently upcasting a derived class function pointer does not affect what happens when it is dereferenced on a derived class (though dereferencing it on a base class gives undefined behaviour).
How exactly does the compiler make this possible?
Although function pointer casts are possible, calling a function through a function type which doesn't match the original type is undefined behavior. The reason function pointer casts are allowed is to support casting to and storing a common type but recovering the correct by casting the function pointer back before calling it. The basic background for this restriction is that even compatible pointers may require adjustment upon use. Hiding the correct signature would imply a suitable trampoline to exist (i.e., the function pointer would need to have additional state).
The paragraph below was extracted from page 420 from Stroustup book "The C++ Programming Language" (third edition):
Because a pointer to a virtual member (s in this example) is a kind of
offset, it does not depend on an object’s location in memory. A
pointer to a virtual member can therefore safely be passed between
different address spaces as long as the same object layout is used in
both. Like pointers to ordinary functions, pointers to nonvirtual
member functions cannot be exchanged between address spaces.
I'm disputing the last sentence in this paragraph. Below, you'll find a code snippet where pointers to nonvirtual member functions, foo() and foo1(), are exchanged between one base object a and a derived object b, without a problem.
What cannot be done is the overloading, of any of the functions in the base, foo() or foo1(), in the derived class, for in this case the compiler will emit an error as shown below.
#include <iostream>
class A
{
int i;
public:
A() : i(1) {}
void foo() { std::cout << i << '\n'; }
void foo1() { std::cout << 2 * i << '\n'; }
};
class B: public A
{
int j;
public:
B() : A(), j(2) {}
// void foo() { std::cout << j << '\n'; }
};
int main()
{
typedef void (A::* PMF)();
PMF p = &B::foo; // error C2374: 'p' redefinition, multiple initialization
// if foo() is overloaded in B.
PMF q = &B::foo1;
B b;
(b.*p)();
(b.*q)();
A a;
(a.*p)();
(a.*q)();
}
That sentence is correct: In (standard) C++, a program, or rather process, has exactly one address space. So as ulidtko pointed out, this sentence is referring to the possibilities of exchanging pointers to virtual vs. non-virtual member functions between address spaces of different processes.
A non-virtual member function of a class is pretty much a standard function with an implicit argument to the object you call it for (the this pointer). As such, it gets assigned some address in your process's address space on loading. Where exactly it ends up in your address space is certainly depending on your platform and whether or not that member function is part of a dynamically linked library. The point is, for two processes it is not necessarily the same address. So passing a pointer to and then executing such a function in another process will potentially 'set your machine on fire (TM)'.
A virtual member function is still pretty much the same as the non-virtual member function as in 'some address in memory you jump to on execution and pass it your this pointer', but it gets called through the virtual function table (vtable) instead of directly. So a pointer to a virtual member function is pretty much just an index into your object's virtual function table. Calling that function does then something along the lines of 'taking your object pointer, maybe increment the pointer to get to the object's vtable and jump to the address at the given index of that table, passing the address of the object itself as the this pointer'. So this indirection via the vtable makes exchanging the pointer to the virtual member function between address spaces work.
Disclaimer: I am leaning somewhat over my "I really know what I am talking about"-comfort zone here. So in case I oversimplified something or worse, yet, engaged in distributing false information, feel free to shred my answer apart ;).
A pointer will always exist as a virtual memory so it is correct, you can check the address and will see there is no physical pointer to the memory address
Because a pointer to a virtual member (s in this example) is a kind of offset, it does not depend on an object’s location in memory. A pointer to a virtual member can therefore safely be passed between different address spaces as long as the same object layout is used in both. Like pointers to ordinary functions, pointers to nonvirtual member functions cannot be exchanged between address spaces.
Assuming I have this class:
class Shape
{
public:
int value;
Shape(int v) : value(v) {};
void draw()
{
cout << "Drawn the element with id: " << value << endl;
}
};
and the following code (which works)
Shape *myShapeObject = new Shape(22);
void (Shape::*drawpntr)();
drawpntr = &Shape::draw;
(myShapeObject ->*drawpntr)();
I have a drawpntr function pointer to a void-returning 0-arguments function member of the class Shape.
First thing I'd like to ask:
drawpntr = &Shape::draw;
the function is a member function and there's no object here.. what address does drawpntr receive? The class shouldn't even exist
I agree with the line
(myShapeObject->*drawpntr)();
because I understand I cannot de-reference a function pointer to a member function (no object -> no function), but what address is actually stored in drawpntr?? There's no object when the
drawpntr = &Shape::draw;
line is invoked.. and the class shouldn't exist either as an entity
All member functions share the same code, so they have the same address in the code segment of memory. Member functions operate on different instances only because they are implicitly passed different values of this pointer. They are not tied in any way to any of the instances on which they operate. The actual value of drawpntr could be determined statically if the function is non-virtual, or dynamically (through the vtable) if the function is virtual.