multiple inheritance: H2 forbid some destructor?

N

nguillot

Hello.

Imagine I have the following class hierarchy:

class A
{ ... };

class B : public virtual class A
{ ... };

class C : public virtual class A
{ ... };

class Concrete : public B, public C
{ ... };

class A is an interface of all concrete objects of type A.
These concrete objects can inherit from several interfaces (here, B
and C), that inherits from A (virtual inheritance).

If all object of type A necessarily have a property (let's say an id),
they can't exist without.
I'd like to store this id in the virtual base:

class A
{
int m_id;
public:
A() {}
A(int p_id) : m_id(p_id) {}
virtual ~A() {}

int GetId() const { return m_id; }
};

class B : public virtual A
{
public:
B() {}
virtual ~B() {}
};

class C : public virtual A
{
public:
C() {}
virtual ~C() {}
};

class Concrete : public B, public C
{
public:
Concrete(int p_id) : A(p_id) {}
virtual ~Concrete() {}
};

It's ok: the users of the class Concrete are only able to create a
Concrete object passing the id.

Now the question: developers have access to Concrete code, but not to
A, B nor C.
How could I forbid them (by compilation error) to add another ctor in
Concrete?

I would expect something like removing the A() {}, but is is needed to
instantiate B and C.

Thanks in advance.
 
N

nephi.allred

Now the question: developers have access to Concrete code, but not to
A, B nor C.
How could I forbid them (by compilation error) to add another ctor in
Concrete?

I would expect something like removing the A() {}, but is is needed to
instantiate B and C.

Thanks in advance.

You don't need A(), B(), or C(). Just add B(int) and C(int)
constructors to B and C.
That is, the following works:

#include <iostream>

class A
{
int id_;
public:
A(int id) : id_(id) {}
virtual ~A() {}

int get_id() const { return id_; }
};

class B : public virtual A
{
public:
B(int id) : A(id) {}
};

class C : public virtual A
{
public:
C(int id) : A(id) {}
};

class Concrete : public B, public C
{
public:
Concrete(int id) : A(id),B(id),C(id) {}
};

int main()
{
Concrete c(0);
std::cout << "Id: " << c.get_id() << std::endl;
return 0;
}
 
B

Bart van Ingen Schenau

If all object of type A necessarily have a property (let's say an id),
they can't exist without.
I'd like to store this id in the virtual base:

class A
{
    int m_id;
public:
    A() {}
    A(int p_id) : m_id(p_id) {}
    virtual ~A() {}

    int GetId() const { return m_id; }

};

class B : public virtual A
{
public:
    B() {}
    virtual ~B() {}

};

class C : public virtual A
{
public:
    C() {}
    virtual ~C() {}

};

class Concrete : public B, public C
{
    public:
        Concrete(int p_id) : A(p_id) {}
        virtual ~Concrete() {}

};

It's ok: the users of the class Concrete are only able to create a
Concrete object passing the id.

Now the question: developers have access to Concrete code, but not to
A, B nor C.
How could I forbid them (by compilation error) to add another ctor in
Concrete?

You can't.
What you can do is make only the A::A(int) constructor available to
these developers.
I would expect something like removing the A() {}, but is is needed to
instantiate B and C.

No, it is not.
The problem is that, due to an oversight, the standard requires that
even in abstract classes the constructor contains an invocation of the
constructor of the virtual base classes, which will never be used
anyway.

There are basically two ways to make A::A() unavailable to the
developers:
1. You remove it completely, and change the constructors of B and C to
call the constructor of A with a dummy value:
B::B() : A(0) {}
Because A is a virtual base-class, only the constructor mentioned in
the most-derived class will be used. All the constructor-calls to A
from intermediate base-classes will be ignored.

2. You make the constructor A::A() private, and declare B and C as
friends of A.
Thanks in advance.

Bart v Ingen Schenau
 
J

James Kanze


[...]
No, it is not.
The problem is that, due to an oversight, the standard
requires that even in abstract classes the constructor
contains an invocation of the constructor of the virtual base
classes, which will never be used anyway.

It's not really an oversight, but the rationale is based on
implementation considerations.
There are basically two ways to make A::A() unavailable to the
developers:
1. You remove it completely, and change the constructors of B and C to
call the constructor of A with a dummy value:
B::B() : A(0) {}
Because A is a virtual base-class, only the constructor mentioned in
the most-derived class will be used. All the constructor-calls to A
from intermediate base-classes will be ignored.
2. You make the constructor A::A() private, and declare B and C as
friends of A.

3. You provide a constructor A::A() which aborts with an error
message.

In these sort of cases, I usually use 3.
 
B

Bart van Ingen Schenau


    [...]
No, it is not.
The problem is that, due to an oversight, the standard
requires that even in abstract classes the constructor
contains an invocation of the constructor of the virtual base
classes, which will never be used anyway.

It's not really an oversight, but the rationale is based on
implementation considerations.

Do you have more information on that? Perhaps a link to the rationale?
I don't see that much difficulty in not requiring a call to the
constructor of a virtual base class if the current class is abstract.
3.  You provide a constructor A::A() which aborts with an error
message.

In these sort of cases, I usually use 3.

It is an option, although I prefer to have a compile-time error over a
runtime error.

Bart v Ingen Schenau
 
J

James Kanze

[...]
I would expect something like removing the A() {}, but is is
needed to instantiate B and C.
No, it is not.
The problem is that, due to an oversight, the standard
requires that even in abstract classes the constructor
contains an invocation of the constructor of the virtual base
classes, which will never be used anyway.
It's not really an oversight, but the rationale is based on
implementation considerations.
Do you have more information on that? Perhaps a link to the
rationale? I don't see that much difficulty in not requiring
a call to the constructor of a virtual base class if the
current class is abstract.

Probably not, but it was felt (I think) that there was no point
in special casing abstract classes in this case.
It is an option, although I prefer to have a compile-time
error over a runtime error.

Me to, but I take what I can get.
 

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,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top