How can C++ achieve dynamic binding yet also static typing?
When you have a pointer to an object, the object may actually be of a class that is derived from the class of the pointer (e.g., a Vehicle* that is actually pointing to a Car object; this is called "polymorphism"). Thus there are two types: the (static) type of the pointer (Vehicle, in this case), and the (dynamic) type of the pointed-to object (Car, in this case).
Static typing means that the legality of a member function invocation is checked at the earliest possible moment: by the compiler at compile time. The compiler uses the static type of the pointer to determine whether the member function invocation is legal. If the type of the pointer can handle the member function, certainly the pointed-to object can handle it as well. E.g., if Vehicle has a certain member function, certainly Car also has that member function since Car is a kind-of Vehicle.
Dynamic binding means that the address of the code in a member function invocation is determined at the last possible moment: based on the dynamic type of the object at run time. It is called "dynamic binding" because the binding to the code that actually gets called is accomplished dynamically (at run time). Dynamic binding is a result of virtual functions.
Related
Normally pointers contain addresses. Why do pointers to member of a class contain offsets?
Talking about pointers to class data members, not pointers to member functions.
A pointer to a class member differs from a regular pointer in that it by itself doesn't actually point at a single location in memory. For example, if you have this setup:
struct MyStruct {
int x;
int y;
};
int MyStruct::* ptr = &MyStruct::y;
Then the pointer ptr isn't actually pointing to a memory address because there is no one object MyStruct::y. Rather, each instance of MyStruct has its own copy of the data member y.
The C++ standard doesn't mandate how pointers-to-member are actually implemented, but a common strategy is to have the pointer-to-member store an offset in bytes from the base of the object to the field in question. That way, when you write something like
MyStruct ms;
ms.*ptr = 137;
The compiler can generate code that says "go to the base address of ms, skip forward a number of bytes specified by the value stored in ptr, then write 137."
Pointers do not have to contain addresses. Pointers are tools to access a certain object indirectly - i.e. write code which would access an object which is unknown at compile time. Example:
a = 10; // It is known at compile time, 10 is written to object a
*pi = 10; // It is not known at compile time where 10 is written to
For normal objects, having address in the pointer is a cheapest way to achieve this goal (however, it won't be the case for C++ script!).
For members, though, normal address wouldn't work - there is no such thing as an address of a member of the class! You can only know the physical address of the member when you have a class object.
So, you have to use some sort of offset, which you than can translate to physical address once the actual object is known.
Pointers-to-members are not pointers.
The C++ type system contains several kinds of compound types, and there are two separate, sibling kinds of compound types that are not related to one another, despite having similar sounding names: Pointers, and "pointers to non-static class members". I'll call the latter "pointer-to-members", with hyphen, to stress that those are not a special kind of pointer, but really something entirely separate.
By the way, the type trait library separates the two concepts via std::is_pointer and std::is_member_pointer.
The two kinds of types serve entirely distinct purposes:
A pointer can represent the address of an object (or function) (or be null). That is, given a dereferenceable pointer, there is an actual concrete thing there that it's pointing at.
A pointer-to-member represents an abstract reference from a class to a non-static class member. Note that there are no objects involved. Such a pointer value does not point at anything concrete, and it has no concept of "dereferencing".
To repeat the final sentence: A pointer-to-member cannot be dereferenced, and it is not in any sense the address of an object. Rather, a pointer-to-member (specifically, to data member) can be applied to an object instance of its class, and together with the object it selects the subobject of that object corresponding to the class member that it represents. (Pointers-to-member-function have a slightly different notion of being applied to an instance; the result of the application is a function call.)
Returning to your question at last: A pointer holds an address of an object or function because it points at an object or function. A pointer-to-member does not hold an address, because it doesn't point at anything; it represents a relationship.
In C++ class members are closely related to their respective class. Thus, a declaration of a pointer to member always contains the class it belongs to. If this results in an offset into the class instance stored in the pointer or something else is not specified.
That way the C++ standard supports nearly any memory addressing scheme like, for example, the non-linear page::offset addresses of early x86, where you had to distinguish between near and far pointers for better performance.
A different case are static class members. They are like global functions/variables in the namespace of the class, eventually with protected or private access restrictions. You may retrieve pointers to static elements and use them like regular pointers.
So say I have a parent class called Libitem and a child class called Book. If I store a pointer of Book inside a map of pointer of Libitem, would I get a pointer of Book back when I try to access it again? Like this:
std::map<int, Libitem*> catalog;
Libitem* b = new Book();
catalog[1] = b;
Libitem* old_book = catalog[1]; //would old_book be a Book or a Libitem? In other word would it
//have all the function of a Book class?
You're retrieving your object like this:
Libitem* old_book = catalog[1];
With that, the compiler only knows that your have a variable named old_book of type pointer to Libitem. And if yourself you read that particular line alone, you'll notice that the only this is the only information you, and the compiler, have at that moment. That moment when you read this line yourself is the compile time. What you can know by reading the code.
When the program actually runs, that variable may point to an object of type Book. But this is only known when the program runs, hence, runtime.
Name lookup happens at compile-time in C++. When you call a function on an object like this:
// Type of an_object is a_struct
a_struct an_object;
an_object.member_function();
The compiler will look at what functions are available inside a_struct. Since the compiler is looking for that name in the struct the variable had been declared, names are really resolved at compile-time.
Let's go back to your case. You have a pointer to Libitem. If you try to access something inside it using the arrow:
old_book->something
To resolve what something is, the compiler will look inside Libitem for it, because the type of old_book is a pointer to Libitem. Even if the pointer points to an instance of a child class, the only thing the compiler knows for sure is that the actual type of the object pointed to is at least Libitem.
Now, you the human knows more than the compiler. You know that the pointer old_book points to an instance of the class Book. You want to access members of Book.
For that, you must explicitly tell to the compiler that you want to use members that come from a subclass. For that to happen, your variable must be of the type Book, so the compiler will look in the appropriated class. To do this, you can cast your variable to another type. Since you cast the variable to a class that is lower in the hierarchy, it is called a downcast. The kind of cast we can use in this case is a dynamic_cast, a cast that will look at runtime what is the actual type of the instance pointed by the pointer:
if (Book* the_old_book = dynamic_cast<Book*>(old_book)) {
// We can use the_old_book here, which his type is Book!
} else {
// The cast failed, the real for of the variable is not Book,
// and the_old_book points to nullptr
}
As you can see, we create a new pointer named the_old_book, which is initialized by the result of the cast. If the real type of the instance pointed by old_book is really not a Book, the cast will fail and return nullptr. Since this happens at runtime, we must validate our new variable using a runtime branch, a if. If the cast fail, the executed block will be the else block.
It would be like any other polymorphic pointer: the functionality of a Libitem, but with any virtual overrides of a Book in the methods.
But: you will not have access to non-virtual methods of Book, whether it be a function/method having a completely different name to any method in Libitem, the same name but different parameter signature to a method in Libitem, or non-virtual cases of having the same name and signature as a method in Libitem.
Of course, if you're a strong-arm kind of person, you can downcast (as pointed out by #SomeProgrammerDude).
I always wondered why there is a stylistic difference between calling a pointer to function vs calling a pointer to a member function in terms of using the de-referencing operator *, i.e.
void(*fptr)()=&f; // pointer to function f()
fptr(); // call f() via pointer to function
Foo foo; // instance of Foo
void (Foo::*fptr)()=&Foo::f; // pointer to member function f()
(foo.*fptr)(); // call foo.f() via pointer to member function
In the first part, you don't use the * operator to call f() via the function pointer, but then one must use it in calling the member function via the pointer, (foo.*fptr)(). Why this difference? Why not just use (foo.fptr)() for consistency? Is there any profound reason or just this is the way C++ was designed?
I'll be quoting stuff from C++ Common Knowledge. The related chapter is titled Pointers to member functions are not pointers. There the author explains why pointers to member functions cannot be implemented as pointer to functions. I'll be using this (the fact that they're different things) as a justification for the different syntax :
When you take the address of a non-static member function, you don't get an address; you get a pointer to member function.
(...)
As with a pointer to data member, we need an object or pointer to an object in order to dereference a pointer to member function. (...) In the case of a pointer to member function, we need the object's address to use as (or to calculate) the value of the this pointer for the function call and possibly for other reasons as well.
Note that there is no such thing as a "virtual" pointer to member function. Virtualness is a property of the member function itself, not the pointer that refers to it.
This is one reason why a pointer to member function cannot be implemented, in general, as a simple pointer to function. The implementation of the pointer to member function must store within itself information as to whether the member function to which it refers is virtual or nonvirtual, information about where to find the appropriate virtual function table pointer, an offset to be added to or subtracted from the function's this pointer and possibly other information. A pointer to member function is commonly implemented as a small structure that contains this information, although many other implementations are also in use.
Dereferencing and calling a pointer to member function usually involves examining the stored information and conditionally executing the appropriate virtual or nonvirtual function calling sequence.
IMHO considering these differences one can justify the syntax difference, although historic design choices could have played their role.
If we simply use foo.fptr, it is the same as how we call a member function, making the compiler complex. Actually, fptr is not a member function of foo. Thus *fptr shows the difference. Maybe the compile can make that happen ,and I think that's how C++ was designed now.
I am reading a book which says:
pass-by-value doesn't work if the function you're sending the argument to has a parameter type which is an abstract base class (ABC). You have to pass the argument (which would be a derived class) by reference.
My questions are:
Why can you not pass the derived class argument by value?
Is there something "internal" to C++ which requires it to be a reference?
Is this to do with the way dynamic binding works?
This is because of object slicing: the abstract class received by value will receive a copy of the object that you are passing; therefore, the derived data members will be sliced off from it, making the object unusable.
When you pass by reference, you are passing the address of the existing object, which is a member of a derived class, all its data members included. This is not specific to C++ - this is a property of passing by value in general.
The dynamic binding is implemented in such a way as to not allow sliced instance receive calls as if it were a complete instance. The vtable pointer is adjusted to dispatch the calls to the member functions of the abstract base class.
Why can you not pass the derived class argument by value?
Simply put, if you have a function that looks like this:
void foo(T t); // Takes a T by value
And you call it this way (whatever v is):
foo(v);
What happens is that the parameter t represents a new object which is copy-initialized from the argument v, as if you were doing:
T t = v;
Now if T is abstract, the above is illegal, because you cannot instantiate an abstract class (by definition). Therefore, the parameter of foo() cannot be copy-initialized from its argument.
Is there something "internal" to C++ which requires it to be a reference?
Something like that. Dynamic polymorphism in C++ can only work through references and pointers. When you try to assign an object of a derived class to an object of a base class, what you get is object slicing.
You can pass a derived class by value. You cannot pass the abstract base class as an argument by value because there is no way to instantiate an abstract class.
In general you want to pass by reference or pointers so you do not slice the object. There is a good answer here: What is object slicing?
When you receive an object by value, its dymanic (its "real" type) type is its static type. It is not possible to have a variable whose dynamic type is an abstract one. No matter how you build it.
Respected Sir!
please explain how c++ implements this dynamic binding a pictorial representation would be more useful in understanding perspective.
or suggest a websigt which contains pictorial representations and full detail about this topic.
let me paste some text for you, any vote is appreciated, :O
[20.2] How can C++ achieve dynamic binding yet also static typing?
When you have a pointer to an object, the object may actually be of a class that is derived from the class of the pointer (e.g., a Vehicle* that is actually pointing to a Car object; this is called "polymorphism"). Thus there are two types: the (static) type of the pointer (Vehicle, in this case), and the (dynamic) type of the pointed-to object (Car, in this case).
Static typing means that the legality of a member function invocation is checked at the earliest possible moment: by the compiler at compile time. The compiler uses the static type of the pointer to determine whether the member function invocation is legal. If the type of the pointer can handle the member function, certainly the pointed-to object can handle it as well. E.g., if Vehicle has a certain member function, certainly Car also has that member function since Car is a kind-of Vehicle.
Dynamic binding means that the address of the code in a member function invocation is determined at the last possible moment: based on the dynamic type of the object at run time. It is called "dynamic binding" because the binding to the code that actually gets called is accomplished dynamically (at run time). Dynamic binding is a result of virtual functions.