15

I am taking a C++ class and have a assignment which requires me to dynamically allocate memory for a struct. I don't recall ever going over this in class and we only briefly touched on the new operator before going on to classes. Now I have to

"Dynamically allocate a student and then prompts the user for student’s first name, a last name, and A - number(ID number). "

my struct is written like

struct Student
{
    string firstName, lastName, aNumber;
    double GPA;
};

I tried Student student1 = new Student; but that doesn't work and I'm unsure as how I do this dynamically with a struct.

sircrisp
  • 1,037
  • 4
  • 17
  • 32

8 Answers8

23

Change you definition to

struct Student 
{
    string firstName, lastName, aNumber;
    double GPA;
};

Notice I have changed the placement of the struct keyword

and you have to do Student* student1 = new Student instead.

When you dynamically allocated memory for a struct you get a pointer to a struct.

Once you are done with the Student you also have to remember to to release the dynamically allocated memory by doing a delete student1. You can use a std::shared_ptr to manage dynamically allocated memory automatically.

parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85
  • 1
    Didn't mean to put Student before struct. But the Student* fixed it, and I have to return the pointer so thanks :] – sircrisp Feb 22 '12 at 15:08
  • I have to return the pointer to main and will delete it when the program ends. – sircrisp Feb 22 '12 at 15:09
  • 2
    @sircrisp This is _exactly_ what `unique_ptr` does. Except it does it for you. Don't use raw pointers, they suck. – Etienne de Martel Feb 22 '12 at 15:12
  • New problem however, now that I have a pointer to a struct how do I fill in the struct members e.g. firstName, lastName. I tried `student.firstName` but it says expression must have a class type. Although I thought I declared it with `Student* student = new Student;` – sircrisp Feb 22 '12 at 15:17
  • 4
    @sircrisp sounds like you really need to consult your class material. Or a [good C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). You need to use `->` to access members through pointers. – R. Martinho Fernandes Feb 22 '12 at 15:27
  • Does that operator have a name? I can't find anything within pointers in my book touching on it and I don't need a whole lecture or chapter on it I just need to see it in use that is the real way I learn. – sircrisp Feb 22 '12 at 15:31
  • It's usually called the "arrow" operator. – Jonathan Grynspan Feb 22 '12 at 16:19
  • 2
    @sircrisp: Judging from this question, this is _not_ the way you learn. – sbi Feb 22 '12 at 17:08
8

This should be what you need:

std::unique_ptr<Student> x(new Student);
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    I doubt it. How can you make such a statement when you don't even know why he is using `new`? – James Kanze Feb 22 '12 at 15:21
  • @James: Because there are virtually no situations whatsoever in which raw `new` is actually justified as opposed to instantly constructing a smart pointer of your choice? – Puppy Feb 22 '12 at 16:26
  • 3
    Nonsense. A lot depends on the application, but most pointers should be raw pointers. (For that matter, you can't avoid it, since `this` is a raw pointer.) `unique_ptr` has very specific semantics; when you need those semantics, it's excellent, but when you don't, it's an error to use it. Since we don't know why he's allocating dynamically (I suspect that in fact, he shouldn't be using dynamic allocation at all), we can't make any assumptions concerning the appropriateness of `unique_ptr`. – James Kanze Feb 22 '12 at 17:17
  • @JamesKanze: I never said anything about raw pointers in general. Only that dynamically allocated resources should always be protected by a resource-managing class such as a smart pointer in the general case. Of course, he likely doesn't need dynamic allocation at all, but requesting dynamic allocation, and no mention of additional requirements, then `unique_ptr` is the first stop. – Puppy Feb 23 '12 at 00:09
  • 1
    And that is the automatism that I'm objecting to. There is no general rule; it all depends on the type, and what you are doing with it. And why you are allocating dynamically. (I've had a number of cases where I just ignore the pointer returned from `new`, for example. Having said that, I'll admit that `unique_ptr`, or `auto_ptr` if can't guarantee an ultra-new compiler, is a good solution in a lot of cases.) – James Kanze Feb 23 '12 at 09:00
  • @JamesKanze: I'm not going to say that `unique_ptr` is the solution to every dynamic allocation problem. It isn't. But it is the *simplest* smart pointer, and therefore, the hardest to get wrong. The compiler will always tell you if you make a mistake in normal usage, and it's easier to manage aliasing, for example. More than that, it's easy to go from `unique_ptr` to `shared_ptr`, for example, it has a constructor and assignment operator for that very purpose, but no facility for the reverse. – Puppy Feb 23 '12 at 13:40
  • The problem isn't the choice of `unique_ptr` rather than some other smart pointer, but using a smart pointer to begin with. Sometimes it's appropriate, sometimes not. Depending on the application, it may rarely be appropriate. (As I said, I've often invoked `new` without doing anything with the resulting pointer.) – James Kanze Feb 23 '12 at 13:57
  • @JamesKanze: Why on earth would you use `new` without taking the returned object? Sounds like a fundamental mis-use to me. – Puppy Feb 23 '12 at 13:57
  • You do a new because you want a new object, generally in response to some external stimulus or input. The new object's constructor, of course, does whatever it has to do to make the object visible to the rest of the application, but there's not necessarily a need for the code constructing it to access it immediately. It's a common pattern. – James Kanze Feb 23 '12 at 14:14
  • @DeadMG: while I disagree with James on this particular case (and agree with you), an example of just `new`-ing and ignoring the resulting pointer is `new Window( "My Window!" )`, where `Window` is very much self-destructing. you would absolutely not want an ordinary smart pointer messing with that! i did once experiment a bit with a novel kind of smart pointer, the *nullable* smart pointer that can point to e.g. a `File` object (so that when an error occurs the pointer is nulled, and its `operator->` throws exception from this point onward), but the standard library has no such advanced thing – Cheers and hth. - Alf Feb 23 '12 at 14:51
  • @JamesKanze: In my opinion that's a bit of an anti-pattern. A factory method would have been a better choice. An object should have no knowledge of it's own lifetime. – Puppy Feb 23 '12 at 15:20
  • @Alf And why would you be dynamically allocating objects which aren't like that? (Most entity objects will be closer to `Window` than to anything else: they will have their own behavior, and react more or less autonomously to "input".) – James Kanze Feb 23 '12 at 17:15
  • @DeadMG It's an anti-pattern for an object to have behavior? That's news to me. Objects (in the OO sense, not in the C++ sense) have behavior, and respond to input or external stimuli (events). Some event triggers the creation of the object, but once that occurs, the object itself takes whatever action is necessary to receive the events which affect it, and reacts to them. – James Kanze Feb 23 '12 at 17:18
  • @James: End of lifetime is an event. It occurs externally to the object. We have a function that responds to that stimulus- the destructor. An object shouldn't produce that itself. Fundamentally, such objects break SRP, because the object does whatever it does, and then it also manages it's own lifetime. That's two responsibilities; one object. Not to mention the dangling pointers problem. – Puppy Feb 24 '12 at 03:28
  • @DeadMG End of lifetime isn't an event; it is the reaction to an event. The object's behavior determines how it reacts to an event. In the absence of complications due to transaction integrity and the like (which may cause the event to be rejected---to become a non-event), the logical reaction to such an event is `delete this`. And dangling pointers may be a problem regardless of who does the `delete`: the usual solution is some form of the observer pattern. (Whatever else may hold, you can't keep the object around just because there's a pointer to it.) – James Kanze Feb 24 '12 at 08:24
  • @James: Every event is only a reaction to another event. That doesn't mean that the decision should be made internally. The conditions under which an object should be destroyed are outside it's own scope. After all, `delete this` is only good if I don't decide to instead place that object in a container. Or on the stack, or a member variable, or an object pool, or... And where I have chosen to place a given object is well outside the realm of it's concerns. Managing your own lifetime requires far too much information, and violates SRP, so it seems like a fairly bad idea. – Puppy Feb 24 '12 at 12:03
  • @DeadMG Events are external to the program: classical input, a request from outside the program, etc. Objects within the program react to these events in different ways. And: the behavior of an object should be implemented in the object. If the object decides to put itself into a container, it removes itself from the container in the destructor; if the object registers itself with other objects (e.g. as an observer), it deregisters in the destructor. And obviously, if the object has behavior, you don't create instances on the stack. – James Kanze Feb 24 '12 at 13:53
  • @JamesKanze: Whether or not the object is in a container is way outside it's scope. It's the choice of whatever code created the object to decide where to place it, and an object should function just fine in multiple places in the same program. It's the job of the Student class to describe Students, not to care about memory allocations and how long it survives. – Puppy Feb 24 '12 at 23:20
  • @DeadMG Whether or not an object is in a specific container is part of its behavior. The object's behavior is (or should be) determined by the object itself, and not some other object; why couple unrelated things. And while you can't make any statements about `Student` without knowing the overall design, in most cases, it seems more reasonable for the `Student` class itself to decide whether it wants to continue or not. (Of course, his `Student` class isn't an object in the OO sense, but a value. And so shouldn't be dynamically allocated to begin with.) – James Kanze Feb 27 '12 at 08:26
5

"Dynamically allocate a student and then prompts the user for student’s first name, a last name, and A - number(ID number). "

This assignment requires you to have a not completely initialized Student object around until you can update it with the information provided by the user. That is a very bad idea in general, because the mere possibility of having a not completely initialized object (e.g. in this case lacking a proper id value) makes the code using that object more complex because it has to check whether, for example, there is a proper id value. And that complexity for proper use, plus failures to recognize that the complexity is needed for proper use, attracts bugs like mad – ungood.

That is why C++, extending C, provided a very strong coupling between allocation and initialization. With a C++ new expression you get either both a successful allocation and a successful complete initialization, or else neither (it cleans up on failure). That is what the question should better teach!

So instead of the given question quoted above, I'm going to teach you acceptable C++ practice (although using new is generally to be avoided), which means answering this modified question:

Prompt the user for student’s first name, a last name, and A - number(ID number), and then dynamically allocate a Student object with these values.

OK, here goes:

// The Dynamic Student, version 1.
// "Prompt the user for student’s first name, a last name, and A - number
// (ID), and then dynamically allocate a `Student` object with these values."

#include <assert.h>         // assert
#include <iostream>         // std::cout,std::endl
#include <string>           // std::string
#include <sstream>          // std::istringstream
#include <stdexcept>        // std::exception, std::runtime_error
#include <stdlib.h>         // EXIT_SUCCESS, EXIT_FAILURE

#define CPP_NO_COPYING_OF( Clazz )      \
    Clazz( Clazz const& );              \
    Clazz& operator=( Clazz const& )

namespace cpp {
    using namespace std;

    bool hopefully( bool const c ) { return c; }
    bool throwX( string const& s ) { throw runtime_error( s ); }

    string lineFromInput()
    {
        string result;
        getline( cin, result )
            || throwX( "lineFromInput: std::getline failed (EOF?)" );
        return result;
    }

    string lineFromInput( string const& prompt )
    {
        cout << prompt;
        return lineFromInput();
    }

    int intFromInput( string const& prompt )
    {
        istringstream   stream( lineFromInput( prompt ) );
        int             result;

        stream >> result
            || throwX( "intFromInput: input line was not a valid number spec" );
        return result;
    }
}  // namespace cpp

namespace blah {
    using namespace std;
    using namespace cpp;

    struct Student
    {
        CPP_NO_COPYING_OF( Student );

        int const       id;
        string const    firstName;
        string const    lastName;

        Student(
            int const       _id,
            string const    _firstName,
            string const    _lastName
            )
            : id( _id ), firstName( _firstName ), lastName( _lastName )
        {}
    };

    Student* studentFromInput()
    {
        cout << "It's -- the Dynamic Student program!" << endl;

        string const    firstName   = lineFromInput( "First name, please? " );
        hopefully( firstName != "" )
            || throwX( "Sorry, the first name can't be nothing." );

        string const    lastName    = lineFromInput( "Last name, please? " );
        hopefully( lastName != "" )
            || throwX( "Sorry, the last name can't be nothing." );

        int const       id          = intFromInput( "And the student id is...? " );
        hopefully( id > 0 )
            || throwX( "Sorry, the id can't be negative or zero." );

        return new Student( id, firstName, lastName );
    }
}  // namespace blah

void cppMain()
{
    using namespace blah;

    Student const* const    pStudent    = studentFromInput();

    try
    {
        // Use the student object, e.g.
        cout
            << "The student is "
            << pStudent->firstName << " " << pStudent->lastName
            << ", with id " << pStudent->id << "."
            << endl;
        // Then:
        delete pStudent;
    }
    catch( std::exception const& )
    {
        delete pStudent;
        throw;      // Rethrows the exception.
    }
}

int main()
{
    using namespace std;

    try
    {
        cppMain();
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        cerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

For each executed new expression (which does allocation and initialization) there should ideally be a corresponding execution of a delete expression, which cleans up and deallocates the memory block so that it can be reused. And the delete expression should ideally be executed even if something fails and throws an exception. Hence the try and catch.

However, coding it like that is error prone and verbose.

Instead, in more idiomatic C++ programming one will use a smart pointer, an object that holds a pointer and provides pointer operations (so it looks like it is a pointer), and whose destructor automatically executes a delete expression when the pointer is no longer used. The C++ standard library has several such smart pointer classes. As a general rule, use the most restrictive smart pointer that you can, because it has least overhead and will most likely support conversion to more general smart pointers, while the opposite is much less likely, downright unlikely.

So in this case, you can use e.g. C++11 std::unique_ptr or if your compiler is old, C++03 std::auto_ptr, both from the <memory> header:

// The Dynamic Student, version 2  --  using smart pointer.
// "Prompt the user for student’s first name, a last name, and A - number
// (ID), and then dynamically allocate a `Student` object with these values."

#include <assert.h>         // assert
#include <iostream>         // std::cout,std::endl
#include <memory>           // std::unique_ptr
#include <string>           // std::string
#include <sstream>          // std::istringstream
#include <stdexcept>        // std::exception, std::runtime_error
#include <stdlib.h>         // EXIT_SUCCESS, EXIT_FAILURE

#define CPP_NO_COPYING_OF( Clazz )      \
    Clazz( Clazz const& );              \
    Clazz& operator=( Clazz const& )

namespace cpp {
    using namespace std;

    bool hopefully( bool const c ) { return c; }
    bool throwX( string const& s ) { throw runtime_error( s ); }

    string lineFromInput()
    {
        string result;
        getline( cin, result )
            || throwX( "lineFromInput: std::getline failed (EOF?)" );
        return result;
    }

    string lineFromInput( string const& prompt )
    {
        cout << prompt;
        return lineFromInput();
    }

    int intFromInput( string const& prompt )
    {
        istringstream   stream( lineFromInput( prompt ) );
        int             result;

        stream >> result
            || throwX( "intFromInput: input line was not a valid number spec" );
        return result;
    }
}  // namespace cpp

namespace blah {
    using namespace std;
    using namespace cpp;

    struct Student
    {
        CPP_NO_COPYING_OF( Student );

        int const       id;
        string const    firstName;
        string const    lastName;

        Student(
            int const       _id,
            string const    _firstName,
            string const    _lastName
            )
            : id( _id ), firstName( _firstName ), lastName( _lastName )
        {}
    };

    unique_ptr<Student> studentFromInput()
    {
        cout << "It's -- the Dynamic Student program!" << endl;

        string const    firstName   = lineFromInput( "First name, please? " );
        hopefully( firstName != "" )
            || throwX( "Sorry, the first name can't be nothing." );

        string const    lastName    = lineFromInput( "Last name, please? " );
        hopefully( lastName != "" )
            || throwX( "Sorry, the last name can't be nothing." );

        int const       id          = intFromInput( "And the student id is...? " );
        hopefully( id > 0 )
            || throwX( "Sorry, the id can't be negative or zero." );

        return unique_ptr<Student>( new Student( id, firstName, lastName ) );
    }
}  // namespace blah

void cppMain()
{
    using namespace blah;

    unique_ptr<Student> const   pStudent    = studentFromInput();

    // Use the student object, e.g.
    cout
        << "The student is "
        << pStudent->firstName << " " << pStudent->lastName
        << ", with id " << pStudent->id << "."
        << endl;
}

int main()
{
    using namespace std;

    try
    {
        cppMain();
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        cerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

But, except for the assignment's requirement to use dynamic allocation, a program with the functionality above would be written without any dynamic allocation or smart pointers. The studentFromInput function would just return a Student object by value, copying. It is almost a paradox, but modern C++ is very heavily based on copying, and still yields pretty fast programs!

Of course, under the hood there are a large number of dirty tricks to avoid that the copying actually happens in the machine code.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • @anonymous downvoter: please explain your downvote so that others can benefit from your insight and not be led astray by the error(s) you spotted. – Cheers and hth. - Alf Feb 22 '12 at 16:40
  • @Alf: I downvoted it, and I think it was a mistake. Not entirely sure why, but if you could edit your answer than I could reverse the downvote. – Puppy Feb 23 '12 at 02:14
  • @Cheersandhth.-Alf: Wow that is an answer, I like how it completes the task and is still unsubmitable as a homework answer. Not best I have seen but good, best was "This should work on x86" `int main(){asm {...}}`" – r_ahlskog Feb 23 '12 at 14:42
3

new returns a pointer to an object... so you'd want

struct Student* student1 = new Student;
Mike Corcoran
  • 14,072
  • 4
  • 37
  • 49
1

struct goes before the name of the structure it defines. :)

What is the error you're seeing when you try new Student? Why doesn't it work?

Jonathan Grynspan
  • 43,286
  • 8
  • 74
  • 104
1

The new statement returns a pointer to the newed instance. So you need to defined the student1 as a pointer.

struct Student * student1 = new Student;
Ade YU
  • 2,292
  • 3
  • 18
  • 28
1

Why are you using new? Just declare an instance of the variable:

Student student1;

Given the definition of Student, it doesn't look like it has identity, and it is certainly copyable, so you should probably never new it.

(I'd also provide it with a constructor, so that it can be initialized correctly from the moment it is defined.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

if you want to understand the concept of syntax or wanna feel of the syntax .. just always treat this struct student = new student syntax. as struct student = new sizeof(student); means you are just simply putting the datatype in the sizeof()-fn for-size-confirmation.

Ajay jangid
  • 711
  • 9
  • 9