Composition using references

A

anongroupaccount

I have an ABC with a protected member that is a reference to an object
of an ABC type:

class ABC
{
public:
virtual ~ABC() = 0;
protected:
AnotherABC& _member;
}

The idea is that my concrete classes will derive from ABC and set
_member in their initialization lists to the specific type of
AnotherABC that the particular class needs. However, this can't be done
as the constructors of the derived classes complain about there being
no default constructor for ABC.

Should I simply replace the reference with a const pointer, or should I
rethink this and save myself from a potentially nasty design flaw? Is
there a design pattern I could use here?
 
T

Thomas Tutone

I have an ABC with a protected member that is a reference to an object
of an ABC type:

class ABC
{
public:
virtual ~ABC() = 0;

your destructor should be protected, not public.
protected:


your data member should be private, not protected.
AnotherABC& _member;
}

The idea is that my concrete classes will derive from ABC and set
_member in their initialization lists to the specific type of
AnotherABC that the particular class needs.

Can't do that. Instead, ABC needs a (protected) constructor that sets
member, and the derived class can call that constructor.
However, this can't be done
as the constructors of the derived classes complain about there being
no default constructor for ABC.

It won't if you follow the above advice.
Should I simply replace the reference with a const pointer, or should I
rethink this and save myself from a potentially nasty design flaw? Is
there a design pattern I could use here?

I've given you code below:

class Other;

class Base {
Other& other_; // this is private
protected:
virtual ~Base() = 0;
Base(Other& o) : other_(o) {}
};

Base::~Base() {}

class Derived : public Base {
public:
Derived(Other& o) : Base(o) {}
};

Best regards,

Tom
 
A

anongroupaccount

Thomas said:
your destructor should be protected, not public.

Why is that? I can understand the constructor being protected, but not
the destructor. Won't this mean that delete won't be able to call the
destructors of inherited classes through the ABC interface?
your data member should be private, not protected.

Why? Must I use access methods in my derived classes when one of the
main points of the ABC is to provide a generalized declaration for my
data members in the class hierarchy? Is it inadvisable to have
protected data at all?
Can't do that. Instead, ABC needs a (protected) constructor that sets
member, and the derived class can call that constructor.


It won't if you follow the above advice.


I've given you code below:

class Other;

class Base {
Other& other_; // this is private
protected:
virtual ~Base() = 0;
Base(Other& o) : other_(o) {}
};

Base::~Base() {}

class Derived : public Base {
public:
Derived(Other& o) : Base(o) {}
};

Best regards,

Tom

Thanks for your help, Tom. I hope my questions don't seem too stupid.
 
B

Bob Hairgrove

I have an ABC with a protected member that is a reference to an object
of an ABC type:

class ABC
{
public:
virtual ~ABC() = 0;
protected:
AnotherABC& _member;

You shouldn't use names with a leading underscore for anything (see
17.4.3.1.2 of the C++ standard).
}

The idea is that my concrete classes will derive from ABC and set
_member in their initialization lists to the specific type of
AnotherABC that the particular class needs. However, this can't be done
as the constructors of the derived classes complain about there being
no default constructor for ABC.

Should I simply replace the reference with a const pointer, or should I
rethink this and save myself from a potentially nasty design flaw? Is
there a design pattern I could use here?

What Thomas said ... however, if the member is to be derived from ABC,
you will probably want to store a reference to ABC and not AnotherABC,
relying on the interface defined in ABC for whatever you need to do
with it. As it is, you cannot initialize this member with an object of
type YetAnotherABC, for example, even if that class also derives from
ABC.
 
R

red floyd

I have an ABC with a protected member that is a reference to an object
of an ABC type:

class ABC
{
public:
virtual ~ABC() = 0;
protected:
AnotherABC& _member;
}

The idea is that my concrete classes will derive from ABC and set
_member in their initialization lists to the specific type of
AnotherABC that the particular class needs. However, this can't be done
as the constructors of the derived classes complain about there being
no default constructor for ABC.

Should I simply replace the reference with a const pointer, or should I
rethink this and save myself from a potentially nasty design flaw? Is
there a design pattern I could use here?

What's wrong with:

class AnotherABC;
class ABC
{
public:
virtual ~ABC() = 0;
protected:
ABC(AnotherABC& param) : _member(param) { }
AnotherABC& _member;
};
 
T

Thomas Tutone

Why is that? I can understand the constructor being protected, but not
the destructor. Won't this mean that delete won't be able to call the
destructors of inherited classes through the ABC interface?

Perhaps you are missing the point. ABC's destructor will never be
called directly - because the destructor is virtual, it will be the
destructor of the derived class that will be called, which will in turn
call the base destructor. The derived destructor has access to the
protected functions of its parent.
Why? Must I use access methods in my derived classes when one of the
main points of the ABC is to provide a generalized declaration for my
data members in the class hierarchy?

Typically, yes.
Is it inadvisable to have
protected data at all?

Typically yes. To quote Stroustrup: "declaring data members protected
is usually a design error." The C++ Programming Language (3rd ed.)
sec. 15.3.1.1

There are exceptions, but that's the usual rule. If your accessor is
inline, there is typically no overhead to using the accessor.
You are correct - you need an accessor here. E.g.:

void doAction() { other.doAction(); }

or

const Other& other() const { return other_; }

or whatever... It's not clear (to me, anyway) what you are trying to
do.
Thanks for your help, Tom. I hope my questions don't seem too stupid.

You're welcome, and they're not stupid. You might consider picking up
a copy of Effective C++ (3d ed.), which includes a lot of good advice.

Best regards,

Tom
 
A

anongroupaccount

Thomas said:
Perhaps you are missing the point. ABC's destructor will never be
called directly - because the destructor is virtual, it will be the
destructor of the derived class that will be called, which will in turn
call the base destructor. The derived destructor has access to the
protected functions of its parent.

Okay, but I want to use ABC as an interface. For example:

ABC* inst = new SomeDerivedClass();
delete inst; // I can't delete it, ABC's destructor is protected

I'm sure this isn't an unusual thing to do, which is why I don't
understand having a protected destructor in ABC.
 
B

Bob Hairgrove

Perhaps you are missing the point. ABC's destructor will never be
called directly - because the destructor is virtual, it will be the
destructor of the derived class that will be called, which will in turn
call the base destructor. The derived destructor has access to the
protected functions of its parent.

This is not true. If the base class' destructor is protected, clients
using the classes will not be able to call delete on a pointer to the
base class. They will, however, be able to call delete on pointers to
the derived classes.

There are situations where this is the desired behavior, e.g. when a
virtual destructor is not desirable or needed, as with private
inheritance.
 
T

Thomas Tutone

Okay, but I want to use ABC as an interface. For example:

ABC* inst = new SomeDerivedClass();
delete inst; // I can't delete it, ABC's destructor is protected

Not true. When you call "delete inst," what does the compiler insert
code to do? Well, first, it calls the destructor for inst. Now inst is
declared as a pointer to ABC. ABC has a virtual destructor. So
instead of calling the destructor for an ABC, the compiler inserts code
to call the destructor for SomeDerivedClass, which presumably has a
_public_ destructor. SomeDerivedClass, in turn, calls ABC's
destructor, which SomeDerivedClass is entitled to do, because its
parent is ABC, and a derived class can call a protected function in its
parent. So your example will work fine, nothwithstanding your comment.
I'm sure this isn't an unusual thing to do, which is why I don't
understand having a protected destructor in ABC.

I hope this makes sense now. If it doesn't, you need to re-review the
concept of virtual functions.

Best regards,

Tom
 
T

Thomas Tutone

Thomas said:
Not true. When you call "delete inst," what does the compiler insert
code to do? Well, first, it calls the destructor for inst. Now inst is
declared as a pointer to ABC. ABC has a virtual destructor. So
instead of calling the destructor for an ABC, the compiler inserts code
to call the destructor for SomeDerivedClass, which presumably has a
_public_ destructor. SomeDerivedClass, in turn, calls ABC's
destructor, which SomeDerivedClass is entitled to do, because its
parent is ABC, and a derived class can call a protected function in its
parent. So your example will work fine, nothwithstanding your comment.

You know what - I'm having a bad day. You are absolutely right and I
am wrong. My apologies for confusing the issue. The destructor should
indeed be public. Glad you're awake, even if I'm not.

Best regards,

Tom
 
A

anongroupaccount

red said:
What's wrong with:

class AnotherABC;
class ABC
{
public:
virtual ~ABC() = 0;
protected:
ABC(AnotherABC& param) : _member(param) { }
AnotherABC& _member;
};

This is exactly what I came up with in the end (I realised this
solution shortly after posting the original question).

Implementation of this idea isn't now a problem, but I have become
absolutely paranoid that I'm making a terrible design decision. I
personally didn't think it was a big problem having protected data
members - they're not public, after all.

My goal in doing things this way is to get things declared as high up
in the class hierarchy as possible. _member is going to exist in all
derived classes - so the best plan of action in my eyes was to put it
in the protected section of ABC rather than declare it as a private
member in every derived class. Is this sane?
 
T

Thomas Tutone

Bob said:
This is not true. If the base class' destructor is protected, clients
using the classes will not be able to call delete on a pointer to the
base class. They will, however, be able to call delete on pointers to
the derived classes.

Yes - My answer was incorrect. Sorry about that.

Best regards,

Tom
 
P

peter steiner

Bob said:
You shouldn't use names with a leading underscore for anything (see
17.4.3.1.2 of the C++ standard).

why not?

one leading underscore followed by a lowercase letter is allowed for
names anywhere but in global and std namespace. class members fall in
neither category.

-- peter
 
B

Bob Hairgrove

I personally didn't think it was a big problem having protected data
members - they're not public, after all.

As long as you are the only developer working on your project, and you
have control over all the code, it is not a problem. However, the
minute more people have to maintain the project, you have what Bjarne
Stroustrup refers to as a "maintenance nightmare" -- chasing down
*all* possible inherited classes and making sure they do not abuse the
protected members. And if they do, sometimes you cannot do anything
about it unless the other developer is in agreement. And there will
always be more classes the next week, or month...

Better to do things right from the very beginning and keep *all* data
members private.
 
M

Mateusz Loskot

peter said:
why not?

one leading underscore followed by a lowercase letter is allowed for
names anywhere but in global and std namespace. class members fall in
neither category.

Yes, it seems to be as you said. But I think the meaning of Standard is
a bit disputable in this case:


17.4.3.1.2 Global names
1. Certain sets of names and function signatures are always reserved
to the implementation:
- Each name that contains a double underscore _ _ or begins with
an underscore followed by an uppercase letter (2.11) is reserved to the
implementation for any use.
- Each name that begins with an underscore is reserved to the
implementation for use as a name in the global namespace.


Note, the only second part refers to global namespace.

Cheers
 
B

Bob Hairgrove

why not?

one leading underscore followed by a lowercase letter is allowed for
names anywhere but in global and std namespace. class members fall in
neither category.

Within class namespace, the global namespace is also visible ... it is
always visible. The whole point is to prevent users of the
implementation from declaring names which conflict with the names used
by the implementation, which can be in the global namespace. You have
no way of excluding those names.
 
P

peter steiner

Bob said:
Within class namespace, the global namespace is also visible ... it is
always visible. The whole point is to prevent users of the
implementation from declaring names which conflict with the names used
by the implementation, which can be in the global namespace. You have
no way of excluding those names.

i stand corrected and enlightened. that should have been obvious, sorry
for the hassle and confusion.

-- peter
 
A

Alf P. Steinbach

* Thomas Tutone:
Yes - My answer was incorrect. Sorry about that.

Well no. Your follow-up explanation was incorrect, but your original
answer was just incomplete.

Declaring the destructor protected in ABC is indeed a Good Thing To Do,
because it means instances of the class _have_ to be dynamically
allocated -- a standard compiler won't then allow anything else.

Of course then the client code must be offered some means to deallocate.
A bad way is to provide a delete-yourself public member function. A
general and good way is to define a common destruction function

template< typename T > callDelete( T* p ) { delete p; }

and make that function (as well as std::auto_ptr) a friend of the class.

As I understand Marshall Cline, this technique will be part of the FAQ
item dealing with ensured dynamic allocation.

Currently that FAQ item only mentions the named constructor idiom.
 
R

Razzer

Mateusz said:
Yes, it seems to be as you said. But I think the meaning of Standard is
a bit disputable in this case:


17.4.3.1.2 Global names
1. Certain sets of names and function signatures are always reserved
to the implementation:
- Each name that contains a double underscore _ _ or begins with
an underscore followed by an uppercase letter (2.11) is reserved to the
implementation for any use.
- Each name that begins with an underscore is reserved to the
implementation for use as a name in the global namespace.


Note, the only second part refers to global namespace.

No. The meaning of the Standard is clear. You cannot use any identifier
with a leading underscore as a name (in a declaration) when the thing
declared is at the global (or std namespace) scope.
 
R

Razzer

Bob said:
Within class namespace, the global namespace is also visible ... it is
always visible. The whole point is to prevent users of the
implementation from declaring names which conflict with the names used
by the implementation, which can be in the global namespace. You have
no way of excluding those names.

The visibility of the object is of no concern. All that matters is if
it uses double-underscore or an underscore followed by a capital letter
at any scope or a single underscore at global scope.

See:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndeepc/html/deep04202000.asp
 

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,009
Latest member
GidgetGamb

Latest Threads

Top