Динамически распределять память для структуры - PullRequest
7 голосов
/ 22 февраля 2012

Я беру класс C ++ и имею назначение, которое требует от меня динамического выделения памяти для структуры.Я не помню, чтобы когда-либо обсуждал это в классе, и мы лишь кратко коснулись оператора new, прежде чем перейти к занятиям.Теперь мне нужно

"Динамически выделить студента, а затем запросить у пользователя имя, фамилию и A-номер студента (идентификационный номер)."

моя структура написана как

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

Я пытался Student student1 = new Student;, но это не работает, и я не уверен, как я делаю это динамически со структурой.

Ответы [ 7 ]

13 голосов
/ 22 февраля 2012

Измените ваше определение на

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

Обратите внимание, что я изменил размещение ключевого слова struct

, и вместо этого вам нужно сделать Student* student1 = new Student.

Когда вы динамически распределяете память для структуры, вы получаете указатель на структуру.

Как только вы закончили с Student , вы также должны помнить, чтобы освободить динамически распределенную память.память, делая delete student1.Вы можете использовать std :: shared_ptr для автоматического управления динамически выделяемой памятью.

7 голосов
/ 22 февраля 2012

Это должно быть то, что вам нужно:

std::unique_ptr<Student> x(new Student);
5 голосов
/ 22 февраля 2012

"Динамически распределить ученика, а затем предложит пользователю ввести имя, фамилию и А-номер студента (идентификационный номер)."

Для этого задания необходимо иметьне полностью инициализированный объект Student, пока вы не сможете обновить его информацией, предоставленной пользователем.В общем, это очень плохая идея, потому что простая возможность иметь не полностью инициализированный объект (например, в этом случае не хватает правильного значения id) делает код, использующий этот объект, более сложным, потому что он должен проверить, например, существует липравильное значение идентификатора.И эта сложность для правильного использования, а также неспособность понять, что сложность необходима для правильного использования, привлекает ошибки как безумные - нехорошо.

Именно поэтому C ++, расширяющий C, обеспечил очень сильную связь между выделением и инициализацией.С помощью выражения C ++ new вы получаете либо и успешное выделение и успешную полную инициализацию, либо ни то, ни другое (оно очищается при сбое).Вот чему лучше учить этот вопрос!

Поэтому вместо приведенного выше вопроса я собираюсь научить вас приемлемой практике C ++ (хотя обычно следует избегать использования new), что означает ответэтот измененный вопрос:

Запрашивает у пользователя имя, фамилию и А-номер студента (идентификационный номер), а затем динамически выделяет объект Student с этими значениями.

ОК, вот так:

// 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;
}

Для каждого исполняемого выражения new (которое выполняет распределение и инициализацию) в идеале должно быть соответствующее выполнение выражения delete, которое очищаети освобождает блок памяти, чтобы его можно было использовать повторно.И выражение delete в идеале должно выполняться, даже если что-то не получается и выдает исключение.Следовательно, try и catch.

Тем не менее, его кодирование может привести к ошибкам и многословно.

Вместо этого в более идиоматическом программировании на С ++ можно использовать умный указатель , объект, который содержит указатель и обеспечивает операции с указателями (таким образом, является указателем), а деструктор автоматически выполняет выражение delete, когда указательбольше не используется.Стандартная библиотека C ++ имеет несколько таких классов интеллектуальных указателей.Как правило, используйте наиболее ограниченный интеллектуальный указатель, который вы можете использовать, поскольку он имеет наименьшие накладные расходы и, скорее всего, будет поддерживать преобразование в более общие интеллектуальные указатели, в то время как обратное гораздо менее вероятно, совершенно маловероятно.

Такв этом случае вы можете использовать, например, C ++ 11 std::unique_ptr или, если ваш компилятор старый, C ++ 03 std::auto_ptr, оба из заголовка <memory>:

// 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;
}

Но, кромедля требования присвоения использовать динамическое распределение, программа с вышеуказанными функциональными возможностями была бы написана без какого-либо динамического выделения или интеллектуальных указателей.Функция studentFromInput просто возвращает объект Student по значению, копируя.Это почти парадокс, но современный C ++ очень сильно основан на копировании и все еще дает довольно быстрые программы!

Конечно, под капотом есть большое количество грязных уловок, чтобы избежать копирования на самом деле происходит в машинном коде.

1 голос
/ 22 февраля 2012

Почему вы используете new?Просто объявите экземпляр переменной:

Student student1;

Учитывая определение Student, он не выглядит так, как будто он имеет идентичность, и, безусловно, его можно копировать, поэтому вы, вероятно, никогда не будете new его.

(я бы также предоставил его конструктором, чтобы он мог правильно инициализироваться с момента его определения.)

1 голос
/ 22 февраля 2012

Оператор new возвращает указатель на экземпляр new ed.Поэтому вам нужно определить student1 как указатель.

struct Student * student1 = new Student;
1 голос
/ 22 февраля 2012

new возвращает указатель на объект ... так что вы хотите

struct Student* student1 = new Student;
1 голос
/ 22 февраля 2012

struct идет перед именем структуры, которую оно определяет. :)

Какую ошибку вы видите при попытке new Student? Почему это не работает?

...