1

So I have read that using new means that you manually have to manage the memory while using automatic variables means that it will get deleted when the variable goes out of scope. How does this work with constructors? If I create an object using an automatic variable, will it be saved or not?

For example, if i have a class:

class University{
    Student s;
public:
    University(int id, char* name){
        Student x(id, name);
        s = x;
    }
};

Would this work? (Assuming I had a properly defined copy constructor for the class Student). If this does work, why would anyone want to use new over this? I'm very new to C++ so apologies if this is a stupid question.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190

4 Answers4

4

Yes, it works. s gets default-constructed first, then copy-assigned only if x is successfully constructed. If an exception is raised and it escapes the University constructor, both s and x are destructed if they were successfully constructed.

That being said, the best way to write this is to not create x at all, and initialize s in the University constructor's initialization list instead:

class University
{
private:
    Student s;

public:
    University(int id, char* name)
        : s(id, name)
    {
    }
};

That being said, your question is about new. The equivalent would look like this:

class University
{
private:
    Student *s;

public:
    University(int id, char* name)
        : s(new Student(id, name))
    {
        try
        {
            // do something that might throw an exception
        }
        catch (...)
        {
            delete s;
            throw;
        }
    }

    ~University()
    {
        delete s;
    }
};

The reason for the try/catch is because if the constructor throws an exception, the destructor will NOT be called.

The above can be replaced with the following using an automatic variable:

class University
{
private:
    std::auto_ptr<Student> s; // or std::unique_ptr if you are using C++11

public:
    University(int id, char* name)
        : s(new Student(id, name))
    {
    }
};

Regardless of whether an exception is thrown, s will be destructed if it was successfully constructed.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Doesn't make much sense from the semantical POV for me. Doesn't have a `University` many `Student`s, not just a single one? – πάντα ῥεῖ Sep 03 '14 at 23:09
  • 1
    @πάνταῥεῖ: This is why we can't have nice things, like identifiers named other than `Foo` and `Bar`. Maybe `s` is short for `student_body_president`, and then there's only one. – Ben Voigt Sep 03 '14 at 23:09
  • 1
    +1 for "This is why we can't have nice things". :) Also @Remy: I hear `auto_ptr` is kinda booo these days. I'd suggest using `unique_ptr` or `shared_ptr`. Otherwise +1. – bitmask Sep 03 '14 at 23:37
  • @bitmask A simple RAII usage of `Student` would serve well, if semantics is taken into account properly :-P (no heap allocation/management needed!). – πάντα ῥεῖ Sep 03 '14 at 23:42
  • @BenVoigt `Foo` and `Bar` have their places, so the answer should either refer to these to abstract out, or handle real world terms like `University` and `Student` correctly. That answer doesn't lead the OP to a finally suitable solution. – πάντα ῥεῖ Sep 03 '14 at 23:45
  • @πάντα ῥεῖ: because he asked about it. And I did provide an RAII alternative. – Remy Lebeau Sep 04 '14 at 00:09
  • @bitmask: yes, if you are using C++11, do use `unique_ptr`, and I did mention that. Before C++11, only `auto_ptr` is available. – Remy Lebeau Sep 04 '14 at 00:10
  • @RemyLebeau Though you've been missing the semantical point. Just redo that using `Foo` and `Bar` for doing abstractions, and avoid the original class names to prevent confusion. – πάντα ῥεῖ Sep 04 '14 at 00:12
  • @πάντα ῥεῖ: Debating semantics is outside the scope of this discussion. The question was about memory management. Period. Stay focused on that. If the OP wants to ask about semantics, let him post a separate question about it. – Remy Lebeau Sep 04 '14 at 00:22
  • @RemyLebeau Your answer leads the OP in a quite non productive limbo though. Either the question should be closed, because it's error prone /flawed from the beginning, or answers should give a _real_ answer that's going to solve the problem suitable for production. – πάντα ῥεῖ Sep 04 '14 at 00:27
  • @RemyLebeau May be you want to join this discussion, I've opened a discussion on meta about these kind of questions and answers: [`Should we take in account implied semantics when answering questions?`](http://meta.stackoverflow.com/questions/270591/should-we-take-in-account-implied-semantics-when-answering-questions) – πάντα ῥεῖ Sep 04 '14 at 03:06
3

Would this work?

Yes, it would work, but the indirection via x using

Student x(id, name);
s = x;

in the constructors body, is completely unnecessary! What I would do, just to condense constructors body code, and avoid reference initialization problems, is using the member initializer list:

class University{
    Student s;
public:
    University(int id, char* name) : s(id,name) {
                                // ^^^^^^^^^^^^ Use member initializer list!
    }
}; 

You don't need that intermediary instance in the constructors body.

If this does work, why would anyone want to use new over this?

There's no certain way to tell, IMHO. Sometimes for certain use cases/requirements it might be desirable to create instances of Student on the heap, rather than use a stack allocated instance.


ATTENTIVELY NOTE:

All of that you have posted here, looks very wrong from a semantical point of view though :-/ ...

A University usually has many Student's subscribed, not just a single one! That means you'll somehow need to keep a list of students within your University instance.
You better should consider to have something like

class University {
    std::vector<Student> students_;
    std::string universityName_;
public:
    University(std::string universityName) : universityName_(universityName) {}
    void addStudent(const Student& newStudent) {
       students_.push_back(newStudent);
    }
    void addStudent(int id, const char* name) {
       students_.push_back(Student(id,name));
    }
};
Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
1

The naive interpretation of your code is that x is an automatic object on the stack of the University(...) constructor. When the constructor call ends, the x object gets destroyed. You're, however, also assigning the x object to s and s is declared as a member variable of the University objects, which will, naively speaking, preserve it.

(Unless the = operator is redefined for the Student class to do something unusual).

If you use the recommended initializer list

University(int id, char* name)
    : s(id, name)
{
}

, the semantics will be different in that that s will be constructed directly from (id, name). If you assign it like you do, then s will get first constructed with its default constructor if it has one, then assigned to from the x variable.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
-1

Yes, that will work. You are creating a temporary student, which is then copied into s, the student object that's member of the university class. As long as the instance of the university class is available, its members are also available. Here are a couple of pointers to make your program better:

  1. Use std::string instead of char*
  2. Passing a student object in the university constructor makes no sense, adding an addStudent method is a better approach in my opinion
  3. Pass const object reference, not primitives, it's better style (more encapsulated) and more efficient because of the reference

For example:

class University{
    Student s;
public:
    University(){}
    void addStudent(const Student& newStudent){
        s = newStudent;
    }

};
ventsyv
  • 3,316
  • 3
  • 27
  • 49
  • All bulls**t! And the second `Student` that's added will leave the 1st one where please?? – πάντα ῥεῖ Sep 03 '14 at 23:11
  • It's just an example, student will probably be a list or something. Or you can call the function setStudent and check if the student has been set already. Student and university are not equivalent objects - it's bad idea to initialize it like that. – ventsyv Sep 03 '14 at 23:16
  • Examples are meaningless, unless they really catch up the real use case (that is implied here using the substantives `University` and `Student`). If you want to make them neutral rather use `Foo` and `Bar`. – πάντα ῥεῖ Sep 03 '14 at 23:27