473,396 Members | 2,154 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Composition using references

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?

Dec 10 '05 #1
31 2864
an**************@googlemail.com wrote:
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

Dec 10 '05 #2

Thomas Tutone wrote:
an**************@googlemail.com wrote:
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.


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?
protected:

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


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?

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


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

Dec 10 '05 #3
On 10 Dec 2005 12:49:47 -0800, "an**************@googlemail.com"
<an**************@googlemail.com> wrote:
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.

--
Bob Hairgrove
No**********@Home.com
Dec 10 '05 #4
an**************@googlemail.com wrote:
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;
};
Dec 10 '05 #5

an**************@googlemail.com wrote:
Thomas Tutone wrote:
an**************@googlemail.com wrote:
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.


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.
protected:

your data member should be private, not protected.
AnotherABC& _member;
}
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.

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) {}


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.
};

Base::~Base() {}

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

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

Dec 10 '05 #6

Thomas Tutone wrote:
an**************@googlemail.com wrote:
Thomas Tutone wrote:
an**************@googlemail.com wrote:
> 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.


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.


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.

Dec 10 '05 #7
On 10 Dec 2005 13:29:14 -0800, "Thomas Tutone"
<Th***********@yahoo.com> wrote:
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.

--
Bob Hairgrove
No**********@Home.com
Dec 10 '05 #8

an**************@googlemail.com wrote:
Thomas Tutone wrote:
an**************@googlemail.com wrote:
Thomas Tutone wrote:
> an**************@googlemail.com wrote:
> > 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.

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.


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

Dec 10 '05 #9

Thomas Tutone wrote:
an**************@googlemail.com wrote:
Thomas Tutone wrote:
an**************@googlemail.com wrote:
> Thomas Tutone wrote:
> > an**************@googlemail.com wrote:
> > > 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.
>
> 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.


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.


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

Dec 10 '05 #10

red floyd wrote:
an**************@googlemail.com wrote:
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;
};


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?

Dec 10 '05 #11

Bob Hairgrove wrote:
On 10 Dec 2005 13:29:14 -0800, "Thomas Tutone"
<Th***********@yahoo.com> wrote:
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.


Yes - My answer was incorrect. Sorry about that.

Best regards,

Tom

Dec 10 '05 #12
Bob Hairgrove wrote:
On 10 Dec 2005 12:49:47 -0800, "an**************@googlemail.com"
<an**************@googlemail.com> wrote:
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).


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

Dec 10 '05 #13
On 10 Dec 2005 13:54:09 -0800, "an**************@googlemail.com"
<an**************@googlemail.com> wrote:
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.

--
Bob Hairgrove
No**********@Home.com
Dec 10 '05 #14
peter steiner wrote:
Bob Hairgrove wrote:

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.


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
--
Mateusz Loskot
http://mateusz.loskot.net

Dec 10 '05 #15
On 10 Dec 2005 14:06:58 -0800, "peter steiner" <pn*******@gmail.com>
wrote:
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.


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.

--
Bob Hairgrove
No**********@Home.com
Dec 10 '05 #16

Bob Hairgrove wrote:
On 10 Dec 2005 14:06:58 -0800, "peter steiner" <pn*******@gmail.com>
wrote:
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.


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

Dec 10 '05 #17
* Thomas Tutone:

Bob Hairgrove wrote:
On 10 Dec 2005 13:29:14 -0800, "Thomas Tutone"
<Th***********@yahoo.com> wrote:
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.


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.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Dec 11 '05 #18

an**************@googlemail.com wrote:
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;
}
This already looks like a bad idea...
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.


You are trying too hard to mix the interface with implementation. There
is *nothing* about the definition of 'ABC' that suggests derived types
need an instance of 'AnotherABC'. Let 'ABC' be a pure abstract class
(no data members), and let derived types use whatever members they need
to implement the interface.

Dec 11 '05 #19

Mateusz Loskot wrote:
peter steiner wrote:
Bob Hairgrove wrote:

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.


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.

Cheers
--
Mateusz Loskot
http://mateusz.loskot.net


Dec 11 '05 #20

Bob Hairgrove wrote:
On 10 Dec 2005 14:06:58 -0800, "peter steiner" <pn*******@gmail.com>
wrote:
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.


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/de...ep04202000.asp

--
Bob Hairgrove
No**********@Home.com


Dec 11 '05 #21
Alf P. Steinbach wrote:
* Thomas Tutone:

Bob Hairgrove wrote:
On 10 Dec 2005 13:29:14 -0800, "Thomas Tutone"
<Th***********@yahoo.com> wrote:

>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.


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.


What about the case of custom allocators? Here the protocol for an
allocator provides a member template for deleting objects allocated
from a 'MyAllocator' (e.g., via placement-new). IME, this is a much
more useful idom than 'callDelete', it does not require friendship, and
it contradicts the "principle" that virtual destructors should be
protected.

class MyAllocator {
public:
virtual ~MyAllocator();

void *allocate(std::size_t size) = 0;
void deallocate(void *buffer) = 0;

template <typename TYPE>
void deleteObject(TYPE *object) {
object->~TYPE();
deallocate(object);
}
};

Dec 11 '05 #22
* da********@warpmail.net:
Alf P. Steinbach wrote:
* Thomas Tutone:

Bob Hairgrove wrote:

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.


What about the case of custom allocators? Here the protocol for an
allocator provides a member template for deleting objects allocated
from a 'MyAllocator' (e.g., via placement-new). IME, this is a much
more useful idom than 'callDelete', it does not require friendship, and
it contradicts the "principle" that virtual destructors should be
protected.

class MyAllocator {
public:
virtual ~MyAllocator();

void *allocate(std::size_t size) = 0;
void deallocate(void *buffer) = 0;

template <typename TYPE>
void deleteObject(TYPE *object) {
object->~TYPE();
deallocate(object);
}
};


You're not making sense to me.

First, there is no "principle" that's based on virtuality of destructor.
There is a technique to ensure dynamic allocation. Dynamic allocation +
inheritance generally implies virtual destructor, but not the other way
around.

Second, the existence of an an allocator class somewhere does not
prevent client code from declaring a static, local or member variable of
any type, and a placement allocation function doesn't, either.

Perhaps you meant to write something more, e.g. an example of a class
using that allocator, where somehow that class was restricted to dynamic
allocation (no such mechanism obvious in what you write), or perhaps
where that class encapsulated all dynamic allocation inside a
value-semantics interface, like the standard container classes (comment
about virtual destructor makes no sense then)?

Btw., allocators are mostly difficult to use properly, and I think in
the above the destructor should offer the no-throw guarantee, and the
allocate member should have typed result, centralizing the casting, like
std::allocator, and the deleteObject function should absolutely not be
templated (ever heard of type safety?); template the class instead.
Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Dec 11 '05 #23

peter steiner wrote:
Bob Hairgrove wrote:
On 10 Dec 2005 14:06:58 -0800, "peter steiner" <pn*******@gmail.com>
wrote:
> 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.


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.


well, rereading the regarding section in the standard i think the
visibility of names is the reason why there are two different
specifications, one pattern reserved everywhere and one in global and
std namespace. the implementation has to use the first kind (__ and
_[A-Z]) for things that have to be visible below the global and std
namespace. seemingly there should be no issue with the other kind of
identifiers (_[a-z]) in user code.

Dec 11 '05 #24
Razzer wrote:
Mateusz Loskot wrote:

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.


Yes, I'm convinced :-)

Cheers
--
Mateusz Loskot
http://mateusz.loskot.net

Dec 11 '05 #25

Alf P. Steinbach wrote:
* da********@warpmail.net:
Alf P. Steinbach wrote:
* Thomas Tutone:
>
> Bob Hairgrove wrote:
>
> 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.
What about the case of custom allocators? Here the protocol for an
allocator provides a member template for deleting objects allocated
from a 'MyAllocator' (e.g., via placement-new). IME, this is a much
more useful idom than 'callDelete', it does not require friendship, and
it contradicts the "principle" that virtual destructors should be
protected.

class MyAllocator {
public:
virtual ~MyAllocator();

void *allocate(std::size_t size) = 0;
void deallocate(void *buffer) = 0;

template <typename TYPE>
void deleteObject(TYPE *object) {
object->~TYPE();
deallocate(object);
}
};


You're not making sense to me.

First, there is no "principle" that's based on virtuality of destructor.
There is a technique to ensure dynamic allocation. Dynamic allocation +
inheritance generally implies virtual destructor, but not the other way
around.


I guess I should have said "suggestion," rather than "principle." The
suggestion that making such destructors protected also proliferates the
use of friends, which, I believe, is not considered a Good Thing to Do,
especially when those friendships are "long-distance" friendships
(i.e., in the Lakosian sense).
Second, the existence of an an allocator class somewhere does not
prevent client code from declaring a static, local or member variable of
any type, and a placement allocation function doesn't, either.
True, but by the same token, I don't think one should encourage the
practice of making (base class) destructors protected and declaring
arbitrary friendships just because some deleter exists somewhere (e.g.,
std::auto_ptr). Also, the suggestion
[...] 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.


clearly does not scale.
Perhaps you meant to write something more, e.g. an example of a class
using that allocator, where somehow that class was restricted to dynamic
allocation (no such mechanism obvious in what you write), or perhaps
where that class encapsulated all dynamic allocation inside a
value-semantics interface, like the standard container classes (comment
about virtual destructor makes no sense then)?
Yes, I probably should have expounded a bit more on the use of the
allocator. What you mention above about encapsulating allocation is
exactly what I had in mind. I'm not sure what you mean by "comment
about virtual destructor makes no sense then," because 'MyAllocator'
instances must be able to delete object through base-class pointers.
Btw., allocators are mostly difficult to use properly, and I think in
the above the destructor should offer the no-throw guarantee, and the
allocate member should have typed result, centralizing the casting, like
std::allocator, and the deleteObject function should absolutely not be
templated (ever heard of type safety?); template the class instead.


The 'allocate' method cannot have a typed result (other than 'char*')
because it is a general-purpose allocator (protocol). What type should
it return?

Dec 12 '05 #26
* da********@warpmail.net:
Alf P. Steinbach wrote:

First, there is no "principle" that's based on virtuality of destructor.
There is a technique to ensure dynamic allocation. Dynamic allocation +
inheritance generally implies virtual destructor, but not the other way
around.
I guess I should have said "suggestion," rather than "principle." The
suggestion that making such destructors protected also proliferates the
use of friends, which, I believe, is not considered a Good Thing to Do,
especially when those friendships are "long-distance" friendships
(i.e., in the Lakosian sense).


It's a strictly limited set of friendships that can be implemented once and
for all by a macro.

Second, the existence of an an allocator class somewhere does not
prevent client code from declaring a static, local or member variable of
any type, and a placement allocation function doesn't, either.


True, but by the same token, I don't think one should encourage the
practice of making (base class) destructors protected and declaring
arbitrary friendships just because some deleter exists somewhere (e.g.,
std::auto_ptr).


Nothing is arbitrary. std::auto_ptr needs friendship because it's primitive.
boost::shared_ptr doesn't.

Also, the suggestion
> [...] 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.

clearly does not scale.


How so? How many different smartpointers that are as primitive as
std::auto_ptr, do you use in a project?
[snip] The 'allocate' method cannot have a typed result (other than 'char*')
because it is a general-purpose allocator (protocol). What type should
it return?


std::allocator is a general-purpose allocator.

std::allocator::allocate() returns T*.

Possibly something like uninitialized_storage<T> could be better than T*.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Dec 12 '05 #27

Alf P. Steinbach wrote:
* da********@warpmail.net:
Alf P. Steinbach wrote:

First, there is no "principle" that's based on virtuality of destructor.
There is a technique to ensure dynamic allocation. Dynamic allocation +
inheritance generally implies virtual destructor, but not the other way
around.
I guess I should have said "suggestion," rather than "principle." The
suggestion that making such destructors protected also proliferates the
use of friends, which, I believe, is not considered a Good Thing to Do,
especially when those friendships are "long-distance" friendships
(i.e., in the Lakosian sense).


It's a strictly limited set of friendships that can be implemented once and
for all by a macro.

Second, the existence of an an allocator class somewhere does not
prevent client code from declaring a static, local or member variable of
any type, and a placement allocation function doesn't, either.


True, but by the same token, I don't think one should encourage the
practice of making (base class) destructors protected and declaring
arbitrary friendships just because some deleter exists somewhere (e.g.,
std::auto_ptr).


Nothing is arbitrary. std::auto_ptr needs friendship because it's primitive.
boost::shared_ptr doesn't.


Why doesn't 'boost:shared_ptr' need friendship?
Also, the suggestion
> > [...] 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.


clearly does not scale.


How so? How many different smartpointers that are as primitive as
std::auto_ptr, do you use in a project?


I think the question is, how many different base classes do you have?
You need to change each one whenever you want to support a new deleter.
Moreover, you never know what your clients want to do. For example, how
do they use custom factories (such as 'MyAllocator')?
[snip]
The 'allocate' method cannot have a typed result (other than 'char*')
because it is a general-purpose allocator (protocol). What type should
it return?


std::allocator is a general-purpose allocator.

std::allocator::allocate() returns T*.

Possibly something like uninitialized_storage<T> could be better than T*.


The point of 'MyAllocator', although not obvious from my description,
is that it encapsulates allocation for an object, meaning that it
allocates both the object, and the memory use by the object. In fact,
you aptly pointed out this usage yourself. For example:

class Foo {
MyAllocator *d_allocator_p; // memory allocator (held)
char *d_buffer_p; // owned
Fee d_fee; // allocates memory

public:
Foo(MyAllocator *basicAllocator)
: d_allocator_p(basicAllocator)
, d_fee(basicAllocator)
{
d_buffer_p = (char*)d_allocator_p->allocate(INITIAL_SIZE);
}

~Foo()
{ d_allocator_p->deallocate(d_buffer_p); }

//...
};

Dec 12 '05 #28

da********@warpmail.net wrote:
Why doesn't 'boost:shared_ptr' need friendship?


because you can pass in a deleter (the second parameter in its
constructor).

Dec 12 '05 #29

da********@warpmail.net wrote:

You are trying too hard to mix the interface with implementation. There
is *nothing* about the definition of 'ABC' that suggests derived types
need an instance of 'AnotherABC'. Let 'ABC' be a pure abstract class
(no data members), and let derived types use whatever members they need
to implement the interface.


We don't know the exact circumstances here but it may well be that
there is a certain amount of the template pattern here.

ABC here obviously has a clear dependency on AnotherABC's interface, it
could well be that the difference is in the lifetime of the objects
(AnotherABC obviously has a longer lifetime) otherwise the classes are
closely coupled.

Dec 12 '05 #30

Earl Purple wrote:
da********@warpmail.net wrote:

You are trying too hard to mix the interface with implementation. There
is *nothing* about the definition of 'ABC' that suggests derived types
need an instance of 'AnotherABC'. Let 'ABC' be a pure abstract class
(no data members), and let derived types use whatever members they need
to implement the interface.
We don't know the exact circumstances here but it may well be that
there is a certain amount of the template pattern here.


This does feel like the template pattern, but seems very misplaced.
ABC here obviously has a clear dependency on AnotherABC's interface, it
could well be that the difference is in the lifetime of the objects
(AnotherABC obviously has a longer lifetime) otherwise the classes are
closely coupled.


The only thing you can tell is that 'ABC' is underspecified. It has an
reference to 'AnotherABC', and a virtual destructor. No other details
are given.

Dec 13 '05 #31
* da********@warpmail.net:

Alf P. Steinbach wrote:

Nothing is arbitrary. std::auto_ptr needs friendship because it's primitive.
boost::shared_ptr doesn't.
Why doesn't 'boost:shared_ptr' need friendship?


Already answered by Earl Purple, but: because you can specify a deleter.

Also, the suggestion

> > > [...] 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.

clearly does not scale.


How so? How many different smartpointers that are as primitive as
std::auto_ptr, do you use in a project?


I think the question is, how many different base classes do you have?
You need to change each one whenever you want to support a new deleter.


Nope.

Moreover, you never know what your clients want to do. For example, how
do they use custom factories (such as 'MyAllocator')?


This is a technique for _restricting_ allocation to 'new'. Another such
technique is to make constructors inaccessible and use factory functions. No
matter which such technique is used, client code is restricted in the
allocation policies it can use, and that's the point.

With the restriction in place other policies can be supported, enabled,
globally by replacing ::operator new, or per class e.g. by inheriting from a
class defining operator new and operator delete.

One can then arrange -- if needed -- that the client code specifies the
allocation policy via a policy template parameter, or via a static allocator
instance, or whatever. Generally allocation policies are per class, set of
classes or globally. I don't see any advantage to allocation policies
specified per-object by the client code.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Dec 13 '05 #32

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Patrick | last post by:
I want to achieve the following behaviour but I am having trouble getting there...bare with me im knew to c++ , so its probably rather trivial! To have a class ClassA, and composed within this...
4
by: fog | last post by:
Given: class A; and B "has-a" A. The composition relationship between A and B can be implemented in three ways: =================== # 1. class B {
7
by: preetam | last post by:
Hi, This question is more towards design than towards c++ details. By looking at books on design patterns and various google threads on the same topic, I see that composition is favoured to...
9
by: Code4u | last post by:
My colleagues and I have been discussing techniques for implementing interfaces in C++. We're looking for a mechanism similar to COM's QueryInterface, in which a certain types of objects can be...
11
by: Milind | last post by:
Hi, I was trying to implement a composition relation, somthing of the following type: class A { public: class B {
4
by: cmrchs | last post by:
Hi, how do I implement aggregation and how composition in C# ? When I say : an Airplane has a Pilot then I use aggregation but when I say : an Airplane has a Cockpit then I use composition. How...
4
by: Frederik Vanderhaegen | last post by:
Hi, Can anyone explain me the difference between aggregation and composition? I know that they both are "whole-part" relationships and that composition parts are destroyed when the composition...
2
by: Gary Wessle | last post by:
Hi what is the difference "in code writing" between aggregation and composition? is the following correct? aggregation (life time of both objects are independent). class A{ /* ... */ };...
4
by: fireball | last post by:
hi, I got confused for a moment about creating data structure for UML composition (strong aggregation) relation one-to-many. I used Rose/DataModeler to do so. <filled_diamond>-------- I...
7
by: snewman18 | last post by:
In learning about design patterns, I've seen discussion about using inheritance when an object's relationship to another object is 'is-a' and composition when the relationship is 'has-a'. Since...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.