Simple const-related question

N

nsdevelop12

Is "const Object* pA = new Object(1)" the same as "const Object A(1)"
in terms of creating const objects that should not be modified through
cast-to-non-const pointers. See the following example:


const int* createInt()
{
return new int(1);
}

void myFunc( const int* const_param )
{
cout << "const_param == " << *const_param << endl;

int *non_const_param = const_cast<int*>( const_param );
*non_const_param = 999;

cout << "const_param == " << *const_param << endl;
}

int main(int argc, char* argv[])
{
int a = 1;
const int b = 1;
int* c = new int(1);
const int* d = new int(1);
const int* e = createInt();

myFunc( &a ); // ok
myFunc( &b ); // bad
myFunc( c ); // ok
myFunc( d ); // bad (?)
myFunc( e ); // bad (?)

return 0;
}
 
N

nsdevelop12

Is "const Object* pA = new Object(1)" the same as "const Object A(1)"
in terms of creating const objects that should not be modified through
cast-to-non-const pointers. See the following example:

const int* createInt()
{
return new int(1);

}

void myFunc( const int* const_param )
{
cout << "const_param == " << *const_param << endl;

int *non_const_param = const_cast<int*>( const_param );
*non_const_param = 999;

cout << "const_param == " << *const_param << endl;

}

int main(int argc, char* argv[])
{
int a = 1;
const int b = 1;
int* c = new int(1);
const int* d = new int(1);
const int* e = createInt();

myFunc( &a ); // ok
myFunc( &b ); // bad
myFunc( c ); // ok
myFunc( d ); // bad (?)
myFunc( e ); // bad (?)

return 0;

}

I guess my real question is what does "new int(1)" or "new MyObject()"
create - is it always non-const object, or does it depend on the
context as in the following:

MyObject *p1 = new MyObject(); // p1 is non-const
const MyObject *p2 = new MyObject(); // p2 is ???

What about:

MyObject* myFunction()
{
return new MyObject();
}

MyObject* p1 = myFunction(); // p1 is non-const
const MyObject *p2 = myFunction(); // p2 is ???
 
D

Daniel T.

Is "const Object* pA = new Object(1)" the same as "const Object A(1)"
in terms of creating const objects that should not be modified through
cast-to-non-const pointers.  See the following example:
const int* createInt()
{
    return new int(1);

void myFunc( const int* const_param )
{
    cout << "const_param == " << *const_param << endl;
    int *non_const_param = const_cast<int*>( const_param );
    *non_const_param = 999;
    cout << "const_param == " << *const_param << endl;

int main(int argc, char* argv[])
{
          int  a = 1;
    const int  b = 1;
          int* c = new int(1);
    const int* d = new int(1);
    const int* e = createInt();
    myFunc( &a ); // ok
    myFunc( &b ); // bad
    myFunc(  c ); // ok
    myFunc(  d ); // bad (?)
    myFunc(  e ); // bad (?)
    return 0;

I guess my real question is what does "new int(1)" or "new MyObject()"
create - is it always non-const object, or does it depend on the
context as in the following:

MyObject *p1 = new MyObject(); // p1 is non-const
const MyObject *p2 = new MyObject(); // p2 is ???

What about:

MyObject* myFunction()
{
    return new MyObject();

}

MyObject* p1 = myFunction(); // p1 is non-const
const MyObject *p2 = myFunction(); // p2 is ???

Get out of the Java habit of putting () after the class name when
calling new. I'm pretty sure that "new Object();" and "new Object;" do
different things.

In answer to your question, the object returned by new is inherently
non-const, but once placed in a const pointer, non-const functions can
no longer be called on that object through that pointer. That doesn't
mean the object is const though, note for example:

Object* o = new Object;
const Object* o2 = o;

o->non_const_func(); // perfectly OK even though the object is being
held in a pointer to const elsewhere.
 
J

Jeff Schwab

Daniel said:
Is "const Object* pA = new Object(1)" the same as "const Object A(1)"
in terms of creating const objects that should not be modified through
cast-to-non-const pointers. See the following example:
const int* createInt()
{
return new int(1);
}
void myFunc( const int* const_param )
{
cout << "const_param == " << *const_param << endl;
int *non_const_param = const_cast<int*>( const_param );
*non_const_param = 999;
cout << "const_param == " << *const_param << endl;
}
int main(int argc, char* argv[])
{
int a = 1;
const int b = 1;
int* c = new int(1);
const int* d = new int(1);
const int* e = createInt();
myFunc( &a ); // ok
myFunc( &b ); // bad
myFunc( c ); // ok
myFunc( d ); // bad (?)
myFunc( e ); // bad (?)
return 0;
}

I believe the last two examples are OK. "int const* p = new int()" is
fundamentally different from "int const i = 5", because the type of the
object pointed to may be in a read-only area of memory in the second
case, but not in the first. I'm not sure why you would ever need this,
though; if you need a non-const pointer to a dynamically allocated
object, why not just hang onto it in the first place? Unnecessary
const_casts defeat the purpose of declaring a pointer-to-const in the
first place.
Get out of the Java habit of putting () after the class name when
calling new. I'm pretty sure that "new Object();"

Allocates a default-constructed Object.
and "new Object;" do

Allocates a potentially uninitialized object, if Object is a POD type.

http://www.research.att.com/~bs/bs_faq2.html#malloc

I don't usually use the parentheses, either, but I guess I probably
should. I also don't usually allocate objects directly with new.
different things.

In answer to your question, the object returned by new is inherently
non-const, but once placed in a const pointer, non-const functions can
no longer be called on that object through that pointer. That doesn't
mean the object is const though, note for example:

Object* o = new Object;
const Object* o2 = o;

o->non_const_func(); // perfectly OK even though the object is being
held in a pointer to const elsewhere.

Agreed.
 
K

Kira Yamato

Daniel said:
On Feb 18, 6:30 pm, (e-mail address removed) wrote:

[...]
Get out of the Java habit of putting () after the class name when
calling new. I'm pretty sure that "new Object();"

Allocates a default-constructed Object.
and "new Object;" do

Allocates a potentially uninitialized object, if Object is a POD type.

http://www.research.att.com/~bs/bs_faq2.html#malloc

I don't usually use the parentheses, either, but I guess I probably
should. I also don't usually allocate objects directly with new.
different things.

Just curious. Instead of directly using new, what do you usually use
to allocate objects?
 
N

nsdevelop12

Daniel said:
On Feb 18, 6:30 pm, (e-mail address removed) wrote:
Is "const Object* pA = new Object(1)" the same as "const Object A(1)"
in terms of creating const objects that should not be modified through
cast-to-non-const pointers. See the following example:
const int* createInt()
{
return new int(1);
}
void myFunc( const int* const_param )
{
cout << "const_param == " << *const_param << endl;
int *non_const_param = const_cast<int*>( const_param );
*non_const_param = 999;
cout << "const_param == " << *const_param << endl;
}
int main(int argc, char* argv[])
{
int a = 1;
const int b = 1;
int* c = new int(1);
const int* d = new int(1);
const int* e = createInt();
myFunc( &a ); // ok
myFunc( &b ); // bad
myFunc( c ); // ok
myFunc( d ); // bad (?)
myFunc( e ); // bad (?)
return 0;
}

I believe the last two examples are OK. "int const* p = new int()" is
fundamentally different from "int const i = 5", because the type of the
object pointed to may be in a read-only area of memory in the second
case, but not in the first. I'm not sure why you would ever need this,
though; if you need a non-const pointer to a dynamically allocated
object, why not just hang onto it in the first place? Unnecessary
const_casts defeat the purpose of declaring a pointer-to-const in the
first place.


Get out of the Java habit of putting () after the class name when
calling new. I'm pretty sure that "new Object();"

Allocates a default-constructed Object.
and "new Object;" do

Allocates a potentially uninitialized object, if Object is a POD type.

http://www.research.att.com/~bs/bs_faq2.html#malloc

I don't usually use the parentheses, either, but I guess I probably
should. I also don't usually allocate objects directly with new.
different things.
In answer to your question, the object returned by new is inherently
non-const, but once placed in a const pointer, non-const functions can
no longer be called on that object through that pointer. That doesn't
mean the object is const though, note for example:
Object* o = new Object;
const Object* o2 = o;
o->non_const_func(); // perfectly OK even though the object is being
held in a pointer to const elsewhere.

Agreed.

Thanks for your help. I'm really trying to avoid the caveat described
here:

http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.13

Using the author's example, I want to make sure that "const Set *s =
new S(...)" is not the same as "const Set s". Although the author
doesn't fully explain why changing "s" in the latter case through an
explicit non-const pointer cast (e.g. "Set *ps =
const_cast<Set*>(&s)") is a problem, I'm assuming its because the
compiler was free to optimize by substituting the value of "s"
wherever it was used in code. If that's the case, then I guess the
former case works because "s" is a pointer to dynamic memory that is
allocated at runtime. But I'm no expert in compilers, so I just
wanted to be sure there were not other tricks at play (like what if
"Set" had an inline constructor or something).
 
J

Jeff Schwab

Kira said:
Daniel said:
On Feb 18, 5:45 pm, (e-mail address removed) wrote:
On Feb 18, 6:30 pm, (e-mail address removed) wrote:

[...]
Get out of the Java habit of putting () after the class name when
calling new. I'm pretty sure that "new Object();"

Allocates a default-constructed Object.
and "new Object;" do

Allocates a potentially uninitialized object, if Object is a POD type.

http://www.research.att.com/~bs/bs_faq2.html#malloc

I don't usually use the parentheses, either, but I guess I probably
should. I also don't usually allocate objects directly with new.
different things.

Just curious. Instead of directly using new, what do you usually use to
allocate objects?

Either the stack (sorry, "auto" storage) or one of the standard
containers. If I have dynamically allocated objects, I generally have
to store pointers to them somewhere anyway, so I let standard containers
deal with managing their lifetimes. For example, if I'll need an
unknown quantity of Foos, I just create a std::list<Foo> and use its
elements. Inserting or deleting list elements does not invalidate
pointers to the other elements.

Allan Holub has an interesting (very opinionated) take on
object-oriented design, which he summarizes in his "Getters and Setters
are Evil" article, and which he describes in depth in the APress book
"Holub on Patterns." The idea is essentially that rather than pulling
data from various sources into a controller object, he tries to push
commands to objects that already have access to the data. The down-side
is that the code implementing a given application is spread all over
creation. The up-side for me has been that because I'm pushing rather
than pulling, object methods rarely need to return pointers to anything.

If an object A knows how to create a Foo, and object B has to do
something with the Foo, A can usually just creates the Foo on the stack,
and pass it to Foo. Suppose the main "controller" function needs an
object of type Foo, but only object a knows how to create a Foo. Rather
than requesting a Foo from object a, the controller function passes a
Receiver object to object a's Foo creation method. Object a creates the
Foo on the stack, then passes it to the receiver. The receiver does
whatever is necessary with the Foo before returning, and the Foo is
automatically destroyed as the stack unwinds back to the controller
function. It's very much like passing an operation to a standard
algorithm like std::for_each, or passing an output iterator to
std::transform.

Suppose main() wants to print a Foo, but only object a can make a Foo.
The "pull" approach might look like this:

/* Pulling a Foo. */
#include <iostream>
#include <vector>

struct Foo { };

std::eek:stream& operator<<(std::eek:stream& out, Foo const& foo) {
return out << "Foo at " << &foo << '\n';
}

struct A {

~A() {
/* Delete any items in foo_. */
// ...
}

Foo* create_foo() {
foo_.push_back(new Foo);
return foo_.back();
}

private:
std::vector<Foo*> foo_;
};

int main() {
A a;

/* Somebody has to keep track of each foo. */
Foo* foo = a.create_foo();

std::cout << *foo;
}

The "push" approach gets rid of the dynamic memory allocation entirely.
A new static type has to be introduced (the receiver), but you no
longer need a container to keep track of the dynamically allocated
objects. In effect, the run-time stack is the container.

/* Pushing a Foo. */
#include <iostream>

struct Foo { };

std::eek:stream& operator<<(std::eek:stream& out, Foo const& foo) {
return out << "Foo at " << &foo << '\n';
}

struct Printing_receiver {

Printing_receiver(std::eek:stream& out):
out_(&out) { }

template<typename T>
void operator()(T const& t) const {
*out_ << t;
}

private:
std::eek:stream* const out_;
};

struct A {
template<typename Foo_receiver>
void create_foo(Foo_receiver const& receiver) {
Foo foo;
receiver(foo);
}
};

int main() {
A a;
a.create_foo(Printing_receiver(std::cout));
}

As long as the creation functions are re-entrant, you can do this as
much as you want, or at least until you run out of stack space. :)
 
J

Jeff Schwab

Daniel said:
On Feb 18, 5:45 pm, (e-mail address removed) wrote:
On Feb 18, 6:30 pm, (e-mail address removed) wrote:
Is "const Object* pA = new Object(1)" the same as "const Object A(1)"
in terms of creating const objects that should not be modified through
cast-to-non-const pointers. See the following example:
const int* createInt()
{
return new int(1);
}
void myFunc( const int* const_param )
{
cout << "const_param == " << *const_param << endl;
int *non_const_param = const_cast<int*>( const_param );
*non_const_param = 999;
cout << "const_param == " << *const_param << endl;
}
int main(int argc, char* argv[])
{
int a = 1;
const int b = 1;
int* c = new int(1);
const int* d = new int(1);
const int* e = createInt();
myFunc( &a ); // ok
myFunc( &b ); // bad
myFunc( c ); // ok
myFunc( d ); // bad (?)
myFunc( e ); // bad (?)
return 0;
}
I believe the last two examples are OK. "int const* p = new int()" is
fundamentally different from "int const i = 5", because the type of the
object pointed to may be in a read-only area of memory in the second
case, but not in the first. I'm not sure why you would ever need this,
though; if you need a non-const pointer to a dynamically allocated
object, why not just hang onto it in the first place? Unnecessary
const_casts defeat the purpose of declaring a pointer-to-const in the
first place.


I guess my real question is what does "new int(1)" or "new MyObject()"
create - is it always non-const object, or does it depend on the
context as in the following:
MyObject *p1 = new MyObject(); // p1 is non-const
const MyObject *p2 = new MyObject(); // p2 is ???
What about:
MyObject* myFunction()
{
return new MyObject();
}
MyObject* p1 = myFunction(); // p1 is non-const
const MyObject *p2 = myFunction(); // p2 is ???
Get out of the Java habit of putting () after the class name when
calling new. I'm pretty sure that "new Object();"
Allocates a default-constructed Object.
and "new Object;" do
Allocates a potentially uninitialized object, if Object is a POD type.

http://www.research.att.com/~bs/bs_faq2.html#malloc

I don't usually use the parentheses, either, but I guess I probably
should. I also don't usually allocate objects directly with new.
different things.
In answer to your question, the object returned by new is inherently
non-const, but once placed in a const pointer, non-const functions can
no longer be called on that object through that pointer. That doesn't
mean the object is const though, note for example:
Object* o = new Object;
const Object* o2 = o;
o->non_const_func(); // perfectly OK even though the object is being
held in a pointer to const elsewhere.
Agreed.

Thanks for your help. I'm really trying to avoid the caveat described
here:

http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.13

Using the author's example, I want to make sure that "const Set *s =
new S(...)" is not the same as "const Set s". Although the author
doesn't fully explain why changing "s" in the latter case through an
explicit non-const pointer cast (e.g. "Set *ps =
const_cast<Set*>(&s)") is a problem, I'm assuming its because the
compiler was free to optimize by substituting the value of "s"
wherever it was used in code. If that's the case, then I guess the
former case works because "s" is a pointer to dynamic memory that is
allocated at runtime. But I'm no expert in compilers, so I just
wanted to be sure there were not other tricks at play (like what if
"Set" had an inline constructor or something).

Why do you need the const_cast? If the method really does change the
object, then the method should be non-const. If the method is
conceptually const, but you need to do a little internal housekeeping on
some data member (e.g. to update a cache), then you can make the member
mutable.
 
J

James Kanze

[...]
Get out of the Java habit of putting () after the class name
when calling new. I'm pretty sure that "new Object();" and
"new Object;" do different things.

If Object has a class type with a user defined constructor, they
do exactly the same thing. Otherwise, new Object() probably
does what you want most of the time.
In answer to your question, the object returned by new is inherently
non-const, but once placed in a const pointer, non-const functions can
no longer be called on that object through that pointer. That doesn't
mean the object is const though, note for example:
Object* o = new Object;
const Object* o2 = o;
o->non_const_func(); // perfectly OK even though the object is being
held in a pointer to const elsewhere.

And to answer the original question, casting away const on the
pointer, and then accessing it, is fully defined behavior.
Technically, you're allowed to allocate a const object, e.g.
"new const Object", but in practice, I don't think any
implementation will make a difference---undefined behavior or
not, the code will actually work exactly as if the object hadn't
been declared const (which, of course, is a permitted behavior
for undefined behavior).
 
J

James Kanze

[...]
Allocates a default-constructed Object.
Allocates a potentially uninitialized object, if Object is a POD type.

I don't usually use the parentheses, either, but I guess I
probably should.

I don't usually use them either, because I learned C++ before
this distinction existed. But then, I don't usually dynamically
allocate objects which don't have a user defined constructor, so
it doesn't make a difference.
I also don't usually allocate objects directly with new.

What do you allocate them with, then?
 
J

James Kanze

Either the stack (sorry, "auto" storage) or one of the
standard containers.

The stack is fine for objects whose lifetime corresponds to a
scope. It doesn't work for objects with arbitrary lifetimes.
And the standard containers all require that the object be
copiable---it's very rare for an object that you'd want to
allocate dynamically to support copy. (If it supported copy,
you'd just allocate it on the stack, passing out copies of it,
rather than maintaining that particular instance alive after
scope has been left.)
If I have dynamically allocated objects, I generally have to
store pointers to them somewhere anyway, so I let standard
containers deal with managing their lifetimes.

Except that if the standard container contains pointers, it
won't (and usually shouldn't) manage their lifetime.
For example, if I'll need an unknown quantity of Foos, I just
create a std::list<Foo> and use its elements. Inserting or
deleting list elements does not invalidate pointers to the
other elements.

In my experience, most dynamically allocated objects are entity
objects. That means that they don't support copy, and so cannot
be put into a list. And of course, they're often polymorphic as
well.
 
J

Jeff Schwab

James said:
The stack is fine for objects whose lifetime corresponds to a
scope. It doesn't work for objects with arbitrary lifetimes.
And the standard containers all require that the object be
copiable---it's very rare for an object that you'd want to
allocate dynamically to support copy. (If it supported copy,
you'd just allocate it on the stack, passing out copies of it,
rather than maintaining that particular instance alive after
scope has been left.)


Except that if the standard container contains pointers, it
won't (and usually shouldn't) manage their lifetime.

Yes, it should. It shouldn't manage the lifetimes of the objects
pointed to by those pointers. But anyway, I didn't mean that I store
pointers in the containers, I meant that I store the objects directly in
the containers.

In my experience, most dynamically allocated objects are entity
objects. That means that they don't support copy, and so cannot
be put into a list.

The abstract (interface) type can't be copied, but the concrete type
generally can.
And of course, they're often polymorphic as
well.

If a factory may need to generate any of ten different concrete types
implementing a particular interface, then it can use ten different lists:

struct obj_interface {
// ...
};

struct obj_factory {

typedef obj_interface* obj_pointer;

obj_pointer create(criteria);

private:
std::list<concrete_obj_a> a_list_;
std::list<concrete_obj_b> b_list_;
// ...
};
 
J

James Kanze

Yes, it should. It shouldn't manage the lifetimes of the objects
pointed to by those pointers.

Yes. I should have been more precise. That's what I meant, of
course.
But anyway, I didn't mean that I store pointers in the
containers, I meant that I store the objects directly in the
containers.

Which is where you loose me, since generally, if you can copy an
object, there's no need to allocate it dynamically. And if you
cannot copy it, you can't store it directly in the container.
The abstract (interface) type can't be copied, but the
concrete type generally can.

Since when? Identity is identity.
If a factory may need to generate any of ten different concrete types
implementing a particular interface, then it can use ten different lists:

Which does what, other than make the code more complicated?
(And of course, the derived types may be derived by the
application.)
 
J

Jeff Schwab

James said:
Yes. I should have been more precise. That's what I meant, of
course.


Which is where you loose me, since generally, if you can copy an
object, there's no need to allocate it dynamically.

Client code can't copy the object (except through object->clone() or the
like), because the client has only a pointer to some abstract base type.
Within the factory that creates the objects, though, there's no mystery
about the exact derived types involved, so they can be created, copied,
or what have you. The factory has to allocate the object dynamically so
that the object will continue to exist after the factory function
returns. The alternative, which I posted elsewhere in this thread, is
for the client code to give the factory a "receiver" to which the
object's address can be passed. The code in the receiver completes
before the factory function returns, and therefore before the object
goes out of scope.

And if you
cannot copy it, you can't store it directly in the container.



Since when? Identity is identity.

Since ever. I don't know what you mean by "identity is identity."
Whether you can copy a particular object almost always depends on
whether you have a pointer to its most-derived type, or to some abstract
base.

The factory that creates objects implementing a particular interface
knows the derived types directly; if it didn't, it couldn't instantiate
them. There's no reason the factory can't copy those objects at will.

Which does what, other than make the code more complicated?
(And of course, the derived types may be derived by the
application.)

It does not (IMO) make the code more complicated. The factory no longer
needs to call new or delete directly, no longer needs an explicitly
declared destructor to delete the objects, and has immediate access to
all objects of a particular derived type.

Suppose a factory stores a single vector of pointers-to-Abstract, rather
than a separate list for each derived type. Now suppose the factory
needs to access all objects of some particular derived type. The
popular solution is to walk through the entire vector, checking each
element's dynamic type with dynamic_cast. Slow and ugly.
 
J

James Kanze

Client code can't copy the object (except through
object->clone() or the like), because the client has only a
pointer to some abstract base type.

That's one situation, appropriate for some applications, but not
everywhere. What happens with the template method pattern, for
example, where the client has to derive himself in order for the
pattern to work. Most of the time, the "client" is responsible
for creating the object.

(I put "client" in quotes, because I'm not sure the word is
really appropriate. Most such objects in my applications are at
the application level---there is no higher level which can be
considered a "client", for which they are a service provider.)
Within the factory that creates the objects, though, there's
no mystery about the exact derived types involved, so they can
be created, copied, or what have you. The factory has to
allocate the object dynamically so that the object will
continue to exist after the factory function returns. The
alternative, which I posted elsewhere in this thread, is for
the client code to give the factory a "receiver" to which the
object's address can be passed. The code in the receiver
completes before the factory function returns, and therefore
before the object goes out of scope.

But the receiver has to know the exact type, which sort of
defeats the role of the factory.

At any rate, I don't see what this buys you. Instead of just
calling delete (on a pointer to the base, or more often, on
this), the code has to find the correct type, to find the
receiver, and remove the object from the receiver. This sounds
like a lot of extra complication (and obfuscation) for nothing.
Since ever. I don't know what you mean by "identity is identity."

Just what I said. An entity object has identity. You can't
copy it---two different objects are two different objects, and
one can't replace the other.

The typical example I use is that of a monetary amount and a
bank account. A monetary amount is a value. You can (and
usually do) copy and assign it. You practically never allocate
it dynamically. A bank account is an entity. A copy of my bank
account is not my bank account, and if you're crediting a
monetary amount to it, you have to do it on the specific object,
not some copy which happens to be equal.

Normally, entity objects do NOT support copy and assignment.
This isn't always strictly true for copy---transaction
management often involves copies in order to implement roll
back. But I've never seen one which supported assignment---in
practice, assignment and polymorphism don't work well together.
Whether you can copy a particular object almost always depends
on whether you have a pointer to its most-derived type, or to
some abstract base.

Whether you can copy a particular object depends on whether it
supports copy. Most polymorphic objects don't (or shouldn't).
And of course, if the object is to be kept in a container, it
needs to support assignment as well.
The factory that creates objects implementing a particular
interface knows the derived types directly; if it didn't, it
couldn't instantiate them. There's no reason the factory
can't copy those objects at will.

Except that by design, the object doesn't support copy and
assignment.

(I have one or two cases in my code where an object supports
copy only because I need to bind it to a reference in some
cases. In such cases, the copy constructor only works if the
object has just been created; once it has been used for anything
else, calling the copy constructor will cause an assertion
failure. Such a scheme could be used in your case. But again:
why?)
It does not (IMO) make the code more complicated. The factory
no longer needs to call new or delete directly, no longer
needs an explicitly declared destructor to delete the objects,
and has immediate access to all objects of a particular
derived type.

Whoever decides to delete the object has to know the most
derived type, in order to find the appropriate container.
That's an enormous complication, and goes against all of the
principles of OO design. And it doesn't buy you anything that I
can see---what's the difference between calling delete (which
every C++ programmer knows will delete the object), and calling
erase on some container (where the relationship between the
pointer one has and the element in the container that is being
deleted may not be obvious)? I'd call using containers for this
obfuscation and additional complication.

And of course, you're not always using a factory---things like
the template method pattern are quite popular in GUI libraries,
for example.
Suppose a factory stores a single vector of
pointers-to-Abstract, rather than a separate list for each
derived type. Now suppose the factory needs to access all
objects of some particular derived type.

Why would it need to do that? Is it a factory, or is it
something else---a factory has nothing to do with the object
once it has created it.
The popular solution is to walk through the entire vector,
checking each element's dynamic type with dynamic_cast.

The usual solution is to forget the derived type entirely once
the object has been created. There are some cases where
dynamic_cast is appropriate, but not very many. The whole point
of derivation (in this case) is that the derived type isA base
type---you need to know the derived type in order to create the
object, but once that's done, you have a base, and that's all
you want to know. (There are exceptions, of course, but they
aren't that frequent.)
 

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

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top