c++base class method accessing subclass field#ICE IV

Surprisingly, the non-virtual base class method can Never know that it’s actually in a subclass instance. It always behaves as a base class method and can’t access subclass data. A few workarounds:

  1. virtual function in subclass
  2. curiously recurring template pattern
  3. down cast the base pointer and directly access the field
#include <iostream>
using namespace std;
struct B {
	char id = 'B';
	virtual ~B() {}
	char getId() {
		return id;
	}
} b;
struct C: public B{
	char id = 'C';
} c;
int main() {
	B* ptr = new C();
	C* ptr2 = dynamic_cast<C*>(ptr);

	cout << "base class nonref: " << b.getId() << endl;
	cout << "sub class nonref: " << c.getId() << endl; //still base class method. 
	// The fact that host object is subclass doesn't matter.

	cout << "base class ptr to subclass instance: " << ptr->getId() << endl; 
	cout << "downcast ptr non-virtual method: " << ptr2->getId() << endl; //B
	cout << "downcast ptr direct access: " << ptr2->id << endl; //C
	return 0;
}
Advertisements

java override: strict on declared parameter types

Best practice – use @Override on the overriding method to request “approval” by the compiler. You will realize that

https://briangordon.github.io/2014/09/covariance-and-contravariance.html is concise –

Rule 1: “return type of the overriding method can (but not c++ [1]) be a subclass of the return type of the overridden method, but the argument types must match exactly”

So almost all discrepancies between parent/child parameter types (like int vs long) will be compiled as overloads. The only exception I know is — overriding method can remove “” from List as the parameter type.

There could be other subtle rules when we consider generics, but in the world without parameterized method signatures, the above Rule 1 is clean and simple.

[[ARM]] P212 explains the Multiple-inheritance would be problematic if this were allowed.

c++method default arg isn’t saved]vtable #Ashish

Google c++ style guide forbids default args in virtual functions.

Ashish said he was asked a common question in multiple c++ interviews. The default arg value is “declared” in base class and (or) derived class but never saved in the vtable. It’s basically resolved statically, at compile time.

If you use a pointer to Base to invoke the virtual method, then the Base default arg value applies unconditionally, even though the subclass method is chosen at run time, via the vtable. Highly confusing.

delete base class ptr,with non-virtual dtor #base dtor runs!

This correct code produces ” ~B ~A” i.e. reverse construction sequence. But what if “virtual” removed?

I said it’s undefined behavior, but what if we know it does do something?

I correctly predicted “~A”, tested in minGw.

class A{
public: virtual ~A(){ cout<<" ~ A"<<endl; }
};
class B: public A{
public: ~B(){ cout<<" ~ B "; }
};
int main(){
A * p = new B;
delete p;
cin.get();
}

"new virtual" modifiers in c#

Unlike java and c++, c# offers new-virtual, which marks a virtual method hiding a base-class virtual method.

* When used in an interface, you must omit the “virtual” since it’s implicit
* When used in a class C, you must spell out “new virtual”. There must be such a Virtual method in the base class B, and there should be a grand-child class D that Overrides this method. P75 [[c# precisely]] illustrates this BCD scenario.

In c#, “override” scares “new” as a method modifier, otherwise “new” plays happily with all other method modifiers —
– new virtual
– new abstract
– new static
– new (nothing) — simple, non-virtual method hiding

why dynamic_cast returns NULL on pointers

https://bintanvictor.wordpress.com/2010/01/14/new-and-dynamic_cast-exceptions-quick-guide/ shows that dynamic_cast of a pointer returns NULL upon failure. Why not throw exception?

A: for efficient test-down-cast. dynamic_cast is the only solution. If it throws exception, the test would have to incur the cost of try/catch.

See P59 [[beyond c++ standard lib]]

template specialization ^ virtual function

To achieve polymorphism, template specialization is an alternative to virtual functions. The way a “behavior” is chosen for a given type is by compile time template instantiation.

In contrast, virtual functions (including essentially all java non-static methods) use run time binding and require additional run time cost and per-instance memory cost –vptr.

In efficiency-sensitive and latency-sensitive apps, the choice is obvious.

#1 famous undefined behavior C++

(See below for smart ptr, template, non-RTTI)

Deleting [1] a derived[3] object via a base[4] pointer is undefined behavior if base[6] class has non-virtual dtor.

This is well-known but it applies to a very specific situation. Many similar situations aren’t described by this language rule —
[1a] This rule requires pointer delete. In contrast, automatic destruction of a non-ref “auto” variable (on stack) is unrelated.

[1b] This rule requires a heap object. Deleting a pointee on stack is a bug but it’s outside this rule.

[1c] This rule is about delete-expression, not delete[]

[3] if the object’s run-time type is base, then this rule is Inapplicable

[4] if the pointer is declared as pointer-to-derived, then Inapplicable

[3,4] if the object run time type is base, AND pointer is declared pointer-to-derived? Inapplicable — compiler or runtime would have failed much earlier before reaching this point.

[6] what if derived class has non-virtual dtor? Well, that implies base non-virtual too. So Yes applicable.

*) P62 [[effC++]] points out that even in the absence of virtual functions (i.e. in a world of non-RTTI objects), you can still hit this UB by deleting a subclass instance via a base pointer.

**) The same example also shows a derived class-template is considered just like a derived class. Let me spell out the entire rule — deleting an instance of a derived-class-template via a pointer to base-class-template is UB if the base class-template has a non-virtual dtor.

What if the pointee is deleted by a smart_ptr destructor? I think you can hit this UB.

virtual function adding complexity #letter to friend

Hi LA

I guess why many C programming teams avoid virtual keyword is because this innocent-looking keyword can cause complexity explosion when mixed with other elements —

– dynamic cast for pointer or reference variables
– pure virtual functions
– slicing — without virtual, slicing is more consistent and easier to debug
– passing a RTTI object by reference — note an object is an RTTI object IFF it has a virtual pointer.
– throwing an RTTI exception object by reference, by pointer or by value — again, things are simpler without virtual
– member function hiding (by accident) — without virtual, the hiding rule is simpler
– non-trivial destructors
– pointer delete — for example, deleting a derived object via a base pointer is undefined behavior if the destructor is non-virtual. If you avoid virtual completely, we would less likely write this kind of buggy code.
– double pointers — pointer to pointer to an RTTI object
– double dispatch — usually involves virtual functions. Double-dispatch may not be very meaningful to non-RTTI objects.
– container of RTTI objects, such as vector of pointer-to-base, where each pointee can be a Derived1 object or Derived2 object… — again, non-RTTI cases are simpler
– templates — remember STL internally uses no virtual function, and perhaps very little inheritance
– smart pointers holding RTTI objects
– private inheritance
– multiple and virtual inheritance

Some experts advocate all base classes should have virtual destructor. In that case, avoiding virtual means avoiding inheritance. That would definitely reduce complexity.

make virtual dtor "pure" just to make the class abstract

Many c++ authors say that if you want to make a non-abstract base class abstract, but don’t have any method to “purify” (i.e. convert to pure-virtual), then a standard practice is to purify dtor by adding “=0” at end of the already-virtual dtor.

(Remember all base classes should have their dtors virtualized)

I wonder why not add a dummy pure-virtual method. (I think in practice this is fine.)

I believe the answer is efficiency, both space-efficiency and possibly run-time efficiency. Adding a dummy pure-virtual adds to every class size and vtbl size, but not instance size. Also every non-abstract sub-class must implement or inherit this dummy method.