Simple const-related question

Discussion in 'C++' started by nsdevelop12@yahoo.com, Feb 18, 2008.

  1. Guest

    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;
    }
    , Feb 18, 2008
    #1
    1. Advertising

  2. Guest

    On Feb 18, 6:30 pm, 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 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 ???
    , Feb 18, 2008
    #2
    1. Advertising

  3. Daniel T. Guest

    On Feb 18, 5:45 pm, wrote:
    > On Feb 18, 6:30 pm, 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 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.
    Daniel T., Feb 19, 2008
    #3
  4. Jeff Schwab Guest

    Daniel T. wrote:
    > On Feb 18, 5:45 pm, wrote:
    >> On Feb 18, 6:30 pm, 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.
    Jeff Schwab, Feb 19, 2008
    #4
  5. Kira Yamato Guest

    On 2008-02-18 20:05:51 -0500, Jeff Schwab <> said:

    > Daniel T. wrote:
    >> On Feb 18, 5:45 pm, wrote:
    >>> On Feb 18, 6:30 pm, 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?

    --

    // kira
    Kira Yamato, Feb 19, 2008
    #5
  6. Guest

    On Feb 18, 9:05 pm, Jeff Schwab <> wrote:
    > Daniel T. wrote:
    > > On Feb 18, 5:45 pm, wrote:
    > >> On Feb 18, 6:30 pm, 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).
    , Feb 19, 2008
    #6
  7. Jeff Schwab Guest

    Kira Yamato wrote:
    > On 2008-02-18 20:05:51 -0500, Jeff Schwab <> said:
    >
    >> Daniel T. wrote:
    >>> On Feb 18, 5:45 pm, wrote:
    >>>> On Feb 18, 6:30 pm, 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. :)
    Jeff Schwab, Feb 19, 2008
    #7
  8. Jeff Schwab Guest

    wrote:
    > On Feb 18, 9:05 pm, Jeff Schwab <> wrote:
    >> Daniel T. wrote:
    >>> On Feb 18, 5:45 pm, wrote:
    >>>> On Feb 18, 6:30 pm, 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.
    Jeff Schwab, Feb 19, 2008
    #8
  9. James Kanze Guest

    On Feb 19, 1:47 am, "Daniel T." <> wrote:
    > On Feb 18, 5:45 pm, wrote:
    > > On Feb 18, 6:30 pm, wrote:


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


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

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Feb 19, 2008
    #9
  10. James Kanze Guest

    On Feb 19, 2:05 am, Jeff Schwab <> wrote:

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

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Feb 19, 2008
    #10
  11. James Kanze Guest

    On Feb 19, 3:26 am, Jeff Schwab <> wrote:
    > Kira Yamato wrote:
    > > On 2008-02-18 20:05:51 -0500, Jeff Schwab <> said:


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


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


    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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Feb 19, 2008
    #11
  12. Jeff Schwab Guest

    James Kanze wrote:
    > On Feb 19, 3:26 am, Jeff Schwab <> wrote:
    >> Kira Yamato wrote:
    >>> On 2008-02-18 20:05:51 -0500, Jeff Schwab <> said:

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

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

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


    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.


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


    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_;
    // ...
    };
    Jeff Schwab, Feb 19, 2008
    #12
  13. James Kanze Guest

    On Feb 19, 6:57 pm, Jeff Schwab <> wrote:
    > James Kanze wrote:
    > > On Feb 19, 3:26 am, Jeff Schwab <> wrote:
    > >> 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.


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

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


    > The abstract (interface) type can't be copied, but the
    > concrete type generally can.


    Since when? Identity is identity.

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


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

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Feb 19, 2008
    #13
  14. Jeff Schwab Guest

    James Kanze wrote:
    > On Feb 19, 6:57 pm, Jeff Schwab <> wrote:
    >> James Kanze wrote:
    >>> On Feb 19, 3:26 am, Jeff Schwab <> wrote:
    >>>> 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.

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


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

    >
    >> The abstract (interface) type can't be copied, but the
    >> concrete type generally can.

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


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

    >
    > 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.
    Jeff Schwab, Feb 20, 2008
    #14
  15. James Kanze Guest

    On Feb 21, 12:05 am, Jeff Schwab <> wrote:
    > James Kanze wrote:
    > > On Feb 19, 6:57 pm, Jeff Schwab <> wrote:
    > >> James Kanze wrote:
    > >>> On Feb 19, 3:26 am, Jeff Schwab <> wrote:
    > >>>> 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.


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


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

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


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


    > >> The abstract (interface) type can't be copied, but the
    > >> concrete type generally can.


    > > Since when? Identity is identity.


    > 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?)

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


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


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

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Feb 21, 2008
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    11
    Views:
    1,098
  2. Javier
    Replies:
    2
    Views:
    556
    James Kanze
    Sep 4, 2007
  3. 0m
    Replies:
    26
    Views:
    1,108
    Tim Rentsch
    Nov 10, 2008
  4. fungus
    Replies:
    13
    Views:
    880
    fungus
    Oct 31, 2008
  5. Replies:
    2
    Views:
    535
    Andrew Koenig
    Feb 9, 2009
Loading...

Share This Page