Why Can't I Do Polymorphism with Normal Variables

Why can't I do polymorphism with normal variables?

The answer is copy semantics.

When you pass an object by value in C++, e.g. printArea(Shape shape) a copy is made of the object you pass. And if you pass a derived class to this function, all that's copied is the base class Shape. If you think about it, there's no way the compiler could do anything else.

Shape shapeCopy = circle;

shapeCopy was declared as a Shape, not a Circle, so all the compiler can do is construct a copy of the Shape part of the object.

Why doesn't polymorphism work without pointers/references?

In C++, an object always has a fixed type and size known at compile-time and (if it can and does have its address taken) always exists at a fixed address for the duration of its lifetime. These are features inherited from C which help make both languages suitable for low-level systems programming. (All of this is subject to the as-if, rule, though: a conforming compiler is free to do whatever it pleases with code as long as it can be proven to have no detectable effect on any behavior of a conforming program that is guaranteed by the standard.)

A virtual function in C++ is defined (more or less, no need for extreme language lawyering) as executing based on the run-time type of an object; when called directly on an object this will always be the compile-time type of the object, so there is no polymorphism when a virtual function is called this way.

Note that this didn't necessarily have to be the case: object types with virtual functions are usually implemented in C++ with a per-object pointer to a table of virtual functions which is unique to each type. If so inclined, a compiler for some hypothetical variant of C++ could implement assignment on objects (such as Base b; b = Derived()) as copying both the contents of the object and the virtual table pointer along with it, which would easily work if both Base and Derived were the same size. In the case that the two were not the same size, the compiler could even insert code that pauses the program for an arbitrary amount of time in order to rearrange memory in the program and update all possible references to that memory in a way that could be proven to have no detectable effect on the semantics of the program, terminating the program if no such rearrangement could be found: this would be very inefficient, though, and could not be guaranteed to ever halt, obviously not desirable features for an assignment operator to have.

So in lieu of the above, polymorphism in C++ is accomplished by allowing references and pointers to objects to reference and point to objects of their declared compile-time types and any subtypes thereof. When a virtual function is called through a reference or pointer, and the compiler cannot prove that the object referenced or pointed to is of a run-time type with a specific known implementation of that virtual function, the compiler inserts code which looks up the correct virtual function to call a run-time. It did not have to be this way, either: references and pointers could have been defined as being non-polymorphic (disallowing them to reference or point to subtypes of their declared types) and forcing the programmer to come up with alternative ways of implementing polymorphism. The latter is clearly possible since it's done all the time in C, but at that point there's not much reason to have a new language at all.

In sum, the semantics of C++ are designed in such a way to allow the high-level abstraction and encapsulation of object-oriented polymorphism while still retaining features (like low-level access and explicit management of memory) which allow it to be suitable for low-level development. You could easily design a language that had some other semantics, but it would not be C++ and would have different benefits and drawbacks.

Polymorphism, why can't I do this?

A vector of B isn't a vector of A even if B is an A (I assume a mixup between A and BaseA)

Try

vector<A> converted(myClass.begin(), myClass.end());

which is probably what you want to express.

(BTW inheriting from vector is bad idea in general, it isn't designed for that.)

Why upcasting does not show runtime polymorphism?

S a = (S)new A();

Let's see what you have here:

  1. variable a, of the reference type S;
  2. an instance creation expression yielding an object of type A, where A extends S;
  3. a reference upcast expression, upcasting the above expression into type S;
  4. the assignment of the result of 3. into the variable a.

What you must keep clear in your mind when reading Java is the distinction between:

  • the type of the object: an object can never change its type. In your example, the object is of type A;
  • the type of the reference: in your example, you converted a reference initially of type A into a reference of type S. You assigned that reference to a.

When you invoke a method on an object, the actual method invoked does not at all depend on the type of the reference, but only on the type of the object itself. The type of your object is A therefore the method in type A is invoked.

On the other hand, when you access an instance variable, polymorphism does not apply and the type of the reference becomes essential. With a.i you access i declared in the supertype S, and with ((A)a).i you access i from A. Note that the class A posseses two instance variables, both named i, and you can refer to each individually.



A note on terminology

The term "type of a reference" is actually a shorthand for the more correct "type of the expression yielding the reference." It is a purely compile-time artifact: there is no type information associated with a reference at runtime, it's just a bit pattern. Contrast this with the type of the object, which is a purely runtime artifact: the compiler doesn't in general know the type of the object involved in an expression, it only makes assertions about it. When such an assertion fails at runtime, the result is a ClassCastException.

Polymorphic class member variable

There is no way to clone polymorphic object without a virtual clone method. So you can either:

  • pass and hold a reference and ensure the printer is not destroyed before the messenger in the code constructing messenger,
  • pass and hold a smart pointer and create the printer instance with new,
  • pass a reference and create printer instance on the heap using clone method or
  • pass a reference to actual type to a template and create instance with new while you still know the type.

The last is what you suggest with C++0x std::unique_ptr, but in this case C++03 std::auto_ptr would do you exactly the same service (i.e. you don't need to move it and they are otherwise the same).

Edit: Ok, um, one more way:

  • Make the printer itself a smart pointer to the actual implementation. Than it's copyable and polymorphic at the same time at the cost of some complexity.

How to use Virtual Functions correctly

You would need to refer to a derived class object using a pointer or a reference to the base class. Call a virtual function for that object and execute the derived class's version of the function:

void attachDoors(Car& iVehicle)
{
cout << "Attaching doors to "
<< iVehicle.getName()
<< " - "
<< iVehicle.getDoorSpecs()
<< endl;
}

Also see: Why do virtual functions need to be passed with a pointer and not by value(of the object)?

What is polymorphism, what is it for, and how is it used?

If you think about the Greek roots of the term, it should become obvious.

  • Poly = many: polygon = many-sided, polystyrene = many styrenes (a), polyglot = many languages, and so on.
  • Morph = change or form: morphology = study of biological form, Morpheus = the Greek god of dreams able to take any form.

So polymorphism is the ability (in programming) to present the same interface for differing underlying forms (data types).

For example, in many languages, integers and floats are implicitly polymorphic since you can add, subtract, multiply and so on, irrespective of the fact that the types are different. They're rarely considered as objects in the usual term.

But, in that same way, a class like BigDecimal or Rational or Imaginary can also provide those operations, even though they operate on different data types.

The classic example is the Shape class and all the classes that can inherit from it (square, circle, dodecahedron, irregular polygon, splat and so on).

With polymorphism, each of these classes will have different underlying data. A point shape needs only two co-ordinates (assuming it's in a two-dimensional space of course). A circle needs a center and radius. A square or rectangle needs two co-ordinates for the top left and bottom right corners and (possibly) a rotation. An irregular polygon needs a series of lines.

By making the class responsible for its code as well as its data, you can achieve polymorphism. In this example, every class would have its own Draw() function and the client code could simply do:

shape.Draw()

to get the correct behavior for any shape.

This is in contrast to the old way of doing things in which the code was separate from the data, and you would have had functions such as drawSquare() and drawCircle().

Object orientation, polymorphism and inheritance are all closely-related concepts and they're vital to know. There have been many "silver bullets" during my long career which basically just fizzled out but the OO paradigm has turned out to be a good one. Learn it, understand it, love it - you'll be glad you did :-)


(a) I originally wrote that as a joke but it turned out to be correct and, therefore, not that funny. The monomer styrene happens to be made from carbon and hydrogen, C8H8, and polystyrene is made from groups of that, (C8H8)n.

Perhaps I should have stated that a polyp was many occurrences of the letter p although, now that I've had to explain the joke, even that doesn't seem funny either.

Sometimes, you should just quit while you're behind :-)

For polymorphic classes, is it OK to use protected instance variables?

is it considered acceptable to make them protected?

Yes. I don't think it will break the law of encapsulation when you use protected modifier. Allow only the subclass to access the instance, we still control what should be accessed by others and who can access the instance.

I could use getter methods in the subclasses to get the instance
variables, but that seems bizarre in this case

In some cases, you want to do some preprocessing before others can access the instance and you can put the preprocessing in the Getter.

Polymorphism with built-in types in Python

Without knowing enough about what you're doing, I'm going to go ahead and agree with your statement

Perhaps I'm just making a mistake by having a collection that mixes class instances with built-in types

If you only need to compare the sign between built-in numbers and your Property instances, it wouldn't be such a big deal (use the cmp function and override comparison operators on your Property class), but I'm betting that's not all you want to do.

Instead, think of better ways to construct your list, if that's what you're really worried about. Here's two examples:

P = Property
myList = [P(0), P(2.3), P(4), P(0,2)]

# or...

def createPropertyList(*args):
return [Property(x) for x in args]

myList = createPropertyList(0,2.3,4,(0,2))


Related Topics



Leave a reply



Submit