private pure virtual function

  • Thread starter Martin Vorbrodt
  • Start date
M

Martin Vorbrodt

Example code in one of my books intrigues me:

class B {
public:
B* Clone() const {
B* p = DoClone();
assert(typeid(*p) == typeid(*this));
return p;
}

protected:
B(const B&);

private:
virtual B* DoClone() const = 0;
};

This will force all derived classes to implement DoClone, or else Clone in
the base class will fail in the assert. Could someone please explain why?

Is typeid of *this for some derived class D of type D or B? I'm sure *p is
of whatever type overrides DoClone, but i'm not sure about *this in the base
class function (even though a derived type is actually in place)... (i may
be getting confused by the whole don't use virtual functions in
constructors, and this pointer advice...)

Also another thing. pure virtual function is private??? What the hell??? Is
it really standard compliant? Can you override it in derived classes
(portably according to the standard) and still keep it private in derived
classes? Does it mean that virtuality of a function applies no matter whta
it's access qualifier is???

Thanks
 
V

Victor Bazarov

Martin said:
Example code in one of my books intrigues me:

class B {
public:
B* Clone() const {
B* p = DoClone();
assert(typeid(*p) == typeid(*this));
return p;
}

protected:
B(const B&);

private:
virtual B* DoClone() const = 0;
};

This will force all derived classes to implement DoClone, or else Clone in
the base class will fail in the assert. Could someone please explain why?

typeid returns 'type_info' for the most derived class. If 'DoClone' does
not create an instance of the same class as 'this' points to when 'Clone'
is called, then the assertion fails.
Is typeid of *this for some derived class D of type D or B?
D&

> I'm sure *p is
of whatever type overrides DoClone, but i'm not sure about *this in the base
class function (even though a derived type is actually in place)... (i may
be getting confused by the whole don't use virtual functions in
constructors, and this pointer advice...)

The assertion is that the most derived classes from both '*this' and '*p'
are the same.
Also another thing. pure virtual function is private??? What the hell??? Is
it really standard compliant?

Why not?
> Can you override it in derived classes
(portably according to the standard) and still keep it private in derived
classes? Does it mean that virtuality of a function applies no matter whta
it's access qualifier is???

Virtuality and access specifiers are orthogonal.

V
 
J

Jim Langston

Martin Vorbrodt said:
Example code in one of my books intrigues me:

class B {
public:
B* Clone() const {
B* p = DoClone();
assert(typeid(*p) == typeid(*this));
return p;
}

protected:
B(const B&);

private:
virtual B* DoClone() const = 0;
};

This will force all derived classes to implement DoClone, or else Clone in
the base class will fail in the assert. Could someone please explain why?

Clone is receiving a B pointer from DoClone. So DoClone is supposed to make
a clone of itself. Clone then checks to make sure that the clone created is
the same type os the instance of the class it's run on. I.E. If DoClone
simply does return new B* the assert will fail, since DoClone is only run on
derieved classes. If DoClone does return new D* the assert should not fail
(presuming D is the derived from B) since the typeid's will match.
Is typeid of *this for some derived class D of type D or B? I'm sure *p is
of whatever type overrides DoClone, but i'm not sure about *this in the
base class function (even though a derived type is actually in place)...
(i may be getting confused by the whole don't use virtual functions in
constructors, and this pointer advice...)

The this pointer will be a type of the instance of the class, whatever that
is. Since B is a pure virtual class, it would have to be some derieved.
Also another thing. pure virtual function is private??? What the hell???
Is it really standard compliant? Can you override it in derived classes
(portably according to the standard) and still keep it private in derived
classes? Does it mean that virtuality of a function applies no matter whta
it's access qualifier is???

pure virtual can be public or private (not sure about protected). Public or
private really doesn't matter as far as pure virtual is concerned I don't
believe.
 
M

Martin Vorbrodt

red floyd said:
Private pure virtual functions don't make much sense, since they can't be
overridden. Protected pure virtuals do. e.g.:

class Base {
public:
void SomeFunc()
{
DoSomethingVirtually();
}
protected:
virtual void DoSomethingVirtually() = 0;
};

not according to Victor ;)
 
A

Alf P. Steinbach

* red floyd:
Private pure virtual functions don't make much sense, since they can't
be overridden.

They can be overridden.

Access is orthogonal to virtuality.

I think this is in the FAQ somewhere, check it out.
 
R

red floyd

Jim said:
pure virtual can be public or private (not sure about protected). Public or
private really doesn't matter as far as pure virtual is concerned I don't
believe.

Private pure virtual functions don't make much sense, since they can't
be overridden. Protected pure virtuals do. e.g.:

class Base {
public:
void SomeFunc()
{
DoSomethingVirtually();
}
protected:
virtual void DoSomethingVirtually() = 0;
};
 
R

red floyd

Alf said:
* red floyd:



They can be overridden.

Access is orthogonal to virtuality.

I think this is in the FAQ somewhere, check it out.

OK, thanks. However, I submit that it's not a good idea, because you
may get unexpected behavior.
 
A

Alf P. Steinbach

* red floyd:
OK, thanks. However, I submit that it's not a good idea, because you
may get unexpected behavior.

Sometimes. Generally it's opposite, because a pure virtual function in
a class T that has an implementation in class T can be confusing: there
is then the question of what that implementation is meant for, e.g., is
it required to be called by any derived class' implementation?

When a pure virtual function has no implementation in the class it's
declared in, as is the most common, then there is no implementation that
_can_ be made available to derived classes. All that a derived class
can do is to implement it or redeclare it.

Hence it's meaningless to make that pure virtual function protected
(there is nothing callable), and so it's IMO better to make it private;
at least that's not meaningless. More generally, unless a member
function's implementation is meant to be callable by derived classes, it
shouldn't be protected. It should then be private.
 
K

Kaz Kylheku

Martin said:
Example code in one of my books intrigues me:

class B {
public:
B* Clone() const {
B* p = DoClone();
assert(typeid(*p) == typeid(*this));
return p;
}

protected:
B(const B&);

private:
virtual B* DoClone() const = 0;
};

This will force all derived classes to implement DoClone, or else Clone in
the base class will fail in the assert. Could someone please explain why?

Well, suppose you have some X class you can call Clone() on an instance
of it it, and everything is cool. You get back an X object, because
there is an X::Clone(). Now you make a derived Y from X, but forget to
override Clone(). When you call Clone() on an instance of Y, you are
calling X::Clone(), which only clones the X parts! You get back an X
object that has no Y parts.

This programmer wanted to avoid that mistake. So firstly, he split the
clone function into two. A base class wrapper, and a DoClone() virtual
which cannot be called directly.
Is typeid of *this for some derived class D of type D or B? I'm sure *p is

typeid uses run-time type information; it gets the actual run-time
type.
of whatever type overrides DoClone, but i'm not sure about *this in the base
class function (even though a derived type is actually in place)... (i may

It doesn't matter. Both p and this are just pointers. The p pointer is
a pointer to the newly cloned object, and this is a pointer to the
object that was cloned. The assertion is simply that they both have the
same type. I.e. the bug of forgetting to override DoClone() has not
occured.
Also another thing. pure virtual function is private??? What the hell??? Is

That's so people could not call DoClone() directly, which would bypass
the safety check.

In a language with auxiliary methods, like Common Lisp, you would just
use an :around method. C++ doesn't have those, so if you want to wrap
additional advice methods around a method call, you split things up
into multiple functions, such as a base class wrapper that calls
virtual "helpers".

Here is how I would do exactly the same thing in that language:

(defmethod clone :around ((obj t))
(let ((new-obj (call-next-method)))
(assert (eq (class-of new-obj) (class-of obj))
new-obj))

The :around keyword specifies that this is an "around" auxiliary
method, and (obj t) means we have one argument called obj, which is of
class T. T is the superclass of all classes. I.e. this method is at the
root of the class hierarchy. Whenever the primary CLONE method is
called, this thing gets control first.

We call the primary CLONE method using (call-next-method), which is a
special function in the object system that an around method to pass
control down the chain. If we don't invoke this, the primary CLONE will
never be called, and around methods can choose to do that.

We catch the cloned object, bind it to the local variable NEW-OBJ, and
then assert that it has exactly the same class as OBJ.

We can now write a couple of primary methods to test. How about one
specialized for integers:

(defmethod clone ((obj integer))
obj)

we "clone" an integer just by returning it. Integers are immutable
objects, so we can return the same object and say we have cloned it.
This works just fine:

(clone 42) --> 42

Now let's write a broken CLONE, which takes a string:

(defmethod clone ((obj string))
(list 1 2)) ;; wrong!

It returns a list instead of cloning the string:

(clone "abc") --> *assertion!*

Boom, the assertion goes off, signaling a condition. Under CLISP the
assertion condition has this text:

"(EQ (CLASS-OF NEW-OBJ) (CLASS-OF OBJ)) must evaluate to a non-NIL
value."

There is no way for the programmer to call the primary CLONE method
directly; it's under the control of the method combination system.

That part is simulated in C++ by renaming the primary function to
DoClone, and then documenting that as being an internal interface, and
please would everyone go through Clone.

So when I saw this C++ class, I right away identified it as a clumsy
around method type of thingy. :)
it really standard compliant? Can you override it in derived classes
(portably according to the standard) and still keep it private in derived
classes? Does it mean that virtuality of a function applies no matter whta
it's access qualifier is???

Access specifiers don't control visibility, only access. You can
inherit a private virtual function, and override it with a public one.
The two are identified as the same function based on their name and
type signature.

Access specifiers only control which call sites are permitted to access
the member.

The real protection in this example comes not from the private:
specifier, but from the renaming of Clone to DoClone().

Someone can derive this class, and implement DoClone() as a public
function, allowing anyone to call it. They are then calling the
improper interface, because they should be going through Clone().
 
K

Kaz Kylheku

red said:
OK, thanks. However, I submit that it's not a good idea, because you
may get unexpected behavior.

Fiddling with access specifiers has no effect on run-time behavior.
Adding an access specifier can turn a program that requires no
diagnostics into a program that requires a diagnostic.
 
K

Kaz Kylheku

Martin said:
Example code in one of my books intrigues me:

class B {
public:
B* Clone() const {
B* p = DoClone();
assert(typeid(*p) == typeid(*this));
return p;
}

protected:
B(const B&);

private:
virtual B* DoClone() const = 0;
};

This will force all derived classes to implement DoClone, or else Clone in
the base class will fail in the assert. Could someone please explain why?

Well, suppose you have some X class you can call Clone() on an instance
of it it, and everything is cool. You get back an X object, because
there is an X::Clone(). Now you make a derived Y from X, but forget to
override Clone(). When you call Clone() on an instance of Y, you are
calling X::Clone(), which only clones the X parts! You get back an X
object that has no Y parts.

This programmer wanted to avoid that mistake. So firstly, he split the
clone function into two. A base class wrapper, and a DoClone() virtual
which should not be called directly.

Is typeid of *this for some derived class D of type D or B? I'm sure *p is

typeid uses run-time type information; it gets the actual run-time
type.
of whatever type overrides DoClone, but i'm not sure about *this in the base
class function (even though a derived type is actually in place)... (i may

It doesn't matter. Both p and this are just pointers. The p pointer is
a pointer to the newly cloned object, and this is a pointer to the
object that was cloned. The assertion is simply that they both have the
same type. I.e. the bug of forgetting to override DoClone() has not
occured.
Also another thing. pure virtual function is private??? What the hell??? Is

It's private to remind people that it's an internal function which
should not be called directly. If it's invoked on the base class, the
call is forbidden by access. But it can still be overriden by public
DoClone() virtuals in derived classes. All derived class writers should
remember to mark their DoClone() override private: otherwise they
expose the function for direct calling.

In a language with auxiliary methods, like Common Lisp, you would just
use an :around method. C++ doesn't have those, so if you want to wrap
additional advice methods around a method call, you split things up
into multiple functions, such as a base class wrapper that calls
virtual "helpers".

Here is how I would do exactly the same thing in that language:

(defmethod clone :around ((obj t))
(let ((new-obj (call-next-method)))
(assert (eq (class-of new-obj) (class-of obj))
new-obj))

The :around keyword specifies that this is an "around" auxiliary
method, and (obj t) means we have one argument called obj, which is of
class T. T is the superclass of all classes. I.e. this method is at the
root of the class hierarchy. Whenever the primary CLONE method is
called, this thing gets control first.

We call the primary CLONE method using (call-next-method), which is a
special function in the object system that an around method to pass
control down the chain. If we don't invoke this, the primary CLONE will
never be called, and around methods can choose to do that.

We catch the cloned object, bind it to the local variable NEW-OBJ, and
then assert that it has exactly the same class as OBJ.

We can now write a couple of primary methods to test. How about one
specialized for integers:

(defmethod clone ((obj integer))
obj)

we "clone" an integer just by returning it. Integers are immutable
objects, so we can return the same object and say we have cloned it.
This works just fine:

(clone 42) --> 42

Now let's write a broken CLONE, which takes a string:

(defmethod clone ((obj string))
(list 1 2)) ;; wrong!

It returns a list instead of cloning the string:

(clone "abc") --> *assertion!*

Boom, the assertion goes off, signaling a condition. Under CLISP the
assertion condition has this text:

"(EQ (CLASS-OF NEW-OBJ) (CLASS-OF OBJ)) must evaluate to a non-NIL
value."

There is no way for the programmer to call the primary CLONE method
directly; it's under the control of the method combination system.

That part is simulated in C++ by renaming the primary function to
DoClone, and then documenting that as being an internal interface, and
please would everyone go through Clone.

So when I saw this C++ class, I right away identified it as a clumsy
around method type of thingy. :)
it really standard compliant? Can you override it in derived classes
(portably according to the standard) and still keep it private in derived
classes? Does it mean that virtuality of a function applies no matter whta
it's access qualifier is???

Access specifiers don't control visibility, only access. You can
inherit a private virtual function, and override it with a public one.
The two are identified as the same function based on their name and
type signature.

When you write a DoClone() in a base class, its subject to whatever
access specifier comes before it. It's just a new function that shadows
the base class DoClone(). The virtuality kicks in separately: because
the base class is virtual, and a shadowing function has the same type
signature, the shadowing one becomes an override.

Access specifiers only control which call sites are permitted to access
the member, not the semantics of inheritance and virtuals.

The real protection in this example comes not from the private:
specifier, but from the renaming of Clone to DoClone(). The derived
class writers can easily forget to make their DoClone() private, which
allows it to be called directly (but not through the base class, where
DoClone is private!)

The users of the class have to cooperate and go through Clone().
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,237
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top