Thread: intricacies using new in constructors

  1. #1
    Registered User
    Join Date
    May 2009
    Posts
    242

    intricacies using new in constructors

    I'm trying to learn this from Prata C++ Primer Plus, ch. 12, and wanted to try to summarize the result here because it seems kind of tricky. Please correct me if I haven't understood this correctly.

    The problem is essentially that if you use new in a constructor (and, accordingly, delete [] in the destructor), there are a lot of implicit constructions that go on and can easily create some problems.

    If I'm understanding it correctly, the cure for these problems is to define explicitly your copy constructor as well as the exact way in which the assignment operator is overloaded. The basic form for the prototype of your copy constructor should be:

    ClassName(const ClassName &);

    And the prototype for overloading the assignment operator should be:

    ClassName & operator=(const ClassName &);

    I won't go into the implementation, which isn't long but also (for me at least) not easy to grasp, but for the moment plan on simply referring back to the book to review how it works whenever I end up dealing with this situation. The main thing is that you're normally (possibly always?) going to need to write both of these out explicitly if you want to allocate memory dynamically in your class constructor.

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    It's often called the rule of three. If you need to write a destructor (in this case for the delete), then you probably also need to write a copy constructor and a copy assignment operator. Your understanding seems fine.

    Note that something many books like yours fail to tell you is that in modern C++ you often don't have to worry about this problem. You will generally use classes that destroy and copy themselves properly already. For example, instead of using new[] to allocate an array (with delete[] to destroy it), you should use vector. It copies itself and destroys itself automatically, and so you won't need your destructor, copy constructor or copy assignment operator.

    Still, it is important to understand the concept and why it matters, so learning how to handle it now is a good thing.

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Daved View Post
    It's often called the rule of three. If you need to write a destructor (in this case for the delete), then you probably also need to write a copy constructor and a copy assignment operator. Your understanding seems fine.
    Practically, despite the common name, I often describe it as the rule of four (or three and a half)

    I have very rarely encountered a class with a non-trivial destructor, copy constructor, and assignment operator that do not also require some other constructor (eg a default constructor, a conversion constructor). After all, it is rather difficult to invoke a copy constructor or assignment operator if an object does not exist to either be copied or used as the right hand side of an assignment.

    Daved has alluded to this, but I'll spell it out. It's also not only usage of operator new that requires the "rule of three" (or whatever) to be applicable. A class that manages any resource introduces the same requirements - memory just happens to be the one people encounter most often.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  4. #4
    Registered User
    Join Date
    May 2009
    Posts
    242
    Thanks guys. That helps a lot.

    Rule of 3 sounds reasonable. On grumpy's point, I would think you'd always want at bare minimum a default constructor to go with these, too, and probably a conversion constructor as well (if my intuitive understanding of that term is correct).

    Vector idea also a good point. I think one thing Prata is trying to do is walk you through at least the basic mechanics behind the way in which the string class is constructed, which I find pretty interesting over and above learning a method where there are no doubt at least SOME instances where it's better not to rely on built-in classes that do a lot of the dirty work for you.

    If an explanation is possible without getting too terribly complex, I'd be interested to hear more about cases other than memory management of "a class that manages any resource" where you need this kind of thing. I'm not immediately seeing what a class like that would be/do.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Aisthesis
    If an explanation is possible without getting too terribly complex, I'd be interested to hear more about cases other than memory management of "a class that manages any resource" where you need this kind of thing. I'm not immediately seeing what a class like that would be/do.
    Well, the copy constructor and copy assignment operator do not always need to be implemented for classes whose objects manage resources. For example, a file is another example of a resource. std::ios_base, which is indirectly inherited by std::istream and std::ostream, disables the copy constructor and copy assignment operator by declaring them private, and there is no need for an implementation to implement them.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    The larch
    Join Date
    May 2006
    Posts
    3,573
    You can manage all kinds of other resources, for example GDI objects. In my last project I have this:

    Code:
    template <class Handle>
    class GdiSelect;
    
    template <class Handle>
    class GdiHandle: boost::noncopyable
    {
        Handle h;
        bool selected;
    public:
        GdiHandle(Handle h): h(h), selected(false) {}
        ~GdiHandle()
        {
            assert(!selected);
            DeleteObject(h);
        }
        friend class GdiSelect<Handle>;
    };
    
    template <class Handle>
    class GdiSelect: boost::noncopyable
    {
        GdiHandle<Handle>* new_handle;
        Handle old_handle;
        HDC hdc;
    public:
        GdiSelect(GdiHandle<Handle>& gdi_handle, HDC dc):
            new_handle(&gdi_handle),
            old_handle(NULL),
            hdc(dc)
        {
            if (new_handle->h) {
                old_handle = static_cast<Handle>(SelectObject(dc, new_handle->h));
                new_handle->selected = true;
            }
        }
        ~GdiSelect()
        {
            if (old_handle) {
                SelectObject(hdc, old_handle);
                new_handle->selected = false;
            }
        }
    };
    GdiHandle automatically deletes the managed object if it goes out of scope. GdiSelect selects the resource into device context and restores the old selection as it goes out of scope. Usage is like this:

    Code:
        ...
        GdiHandle<HFONT> header_font(create_header_font(hdc));
        while (printing_pages) {
            ...
                //draw header.
                //GdiSelect restores the previous font in destructor, hence another scope
                {
                    GdiSelect<HFONT> select_header_font(header_font, hdc);
                    if (!DrawText(hdc, filename.c_str(), filename.size(), &header, DT_LEFT) ||
                        !DrawText(hdc, page_number.c_str(), page_number.size(), &header, DT_RIGHT)) {
                        //failed printing header. Do nothing?
                    }
                }
                ...
        }
    It also takes another approach to the rule of three: make the class non-copyable and non-assignable (declare the copy constructor/operator= private or inherit from such a class).
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Constructors
    By MarlonDean in forum C++ Programming
    Replies: 3
    Last Post: 06-18-2008, 01:21 AM
  2. constructors, arrays, and new
    By Thantos in forum C++ Programming
    Replies: 6
    Last Post: 05-30-2004, 06:21 PM
  3. Throwing exceptions with constructors
    By nickname_changed in forum C++ Programming
    Replies: 14
    Last Post: 07-08-2003, 09:21 AM
  4. Copy constructors and private constructors
    By Eibro in forum C++ Programming
    Replies: 5
    Last Post: 11-24-2002, 10:16 AM
  5. Default Constructors
    By MethodMan in forum C++ Programming
    Replies: 2
    Last Post: 10-25-2002, 06:30 PM