【问题标题】:Dynamically allocate memory for struct为结构动态分配内存
【发布时间】:2012-03-12 22:16:17
【问题描述】:

我正在学习 C++ 课程并且有一个任务要求我为结构动态分配内存。我不记得曾经在课堂上讨论过这个问题,我们只是在上课之前简单地触及了new 运算符。现在我必须

"动态分配一个学生,然后提示用户输入学生的名字、姓氏和A-号码(身份证号)。"

我的结构是这样写的

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

我尝试了Student student1 = new Student;,但这不起作用,我不确定如何使用结构动态执行此操作。

【问题讨论】:

    标签: c++ dynamic struct


    【解决方案1】:

    把你的定义改成

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

    请注意,我更改了 struct 关键字的位置

    你必须改用Student* student1 = new Student

    当你为一个结构动态分配内存时,你会得到一个指向一个结构的指针。

    完成Student后,您还必须记住通过delete student1 释放动态分配的内存。您可以使用 std::shared_ptr 自动管理动态分配的内存。

    【讨论】:

    • 并不是要把 Student 放在 struct 之前。但是学生*修复了它,我必须返回指针,所以谢谢:]
    • 我必须将指针返回给main,并在程序结束时将其删除。
    • @sircrisp 这正是 unique_ptr 所做的。除了它为你做。不要使用原始指针,它们很烂。
    • @sircrisp 听起来你真的需要查阅你的课程材料。或good C++ book。需要使用->通过指针访问成员。
    • @sircrisp:从这个问题来看,这不是你的学习方式。
    【解决方案2】:

    这应该是你需要的:

    std::unique_ptr<Student> x(new Student);
    

    【讨论】:

    • 我对此表示怀疑。当你甚至不知道他为什么使用new时,你怎么能做出这样的声明?
    • @James:因为几乎没有任何情况可以证明原始new 是合理的,而不是立即构建您选择的智能指针?
    • 废话。很大程度上取决于应用程序,但大多数指针应该是原始指针。 (就此而言,您无法避免它,因为this 是一个原始指针。)unique_ptr 具有非常具体的语义;当您需要这些语义时,它非常好,但是当您不需要时,使用它是错误的。由于我们不知道他为什么要动态分配(我怀疑实际上他根本不应该使用动态分配),所以我们不能对unique_ptr 的适当性做出任何假设。
    • @JamesKanze:一般来说,我从来没有说过任何关于原始指针的事情。只有动态分配的资源才应该始终受到资源管理类的保护,例如在一般情况下的智能指针。当然,他很可能根本不需要动态分配,而是要求动态分配,并且没有提到额外的要求,那么unique_ptr是第一站。
    • 这就是我反对的自动化。没有一般规则;这一切都取决于类型,以及你用它做什么。以及为什么要动态分配。 (例如,我有很多情况我只是忽略从new 返回的指针。话虽如此,我承认unique_ptrauto_ptr 如果不能保证超新编译器,在很多情况下是一个很好的解决方案。)
    【解决方案3】:

    "动态分配一个学生,然后提示用户输入学生的名字、姓氏和A-号码(身份证号)。"

    此分配要求您有一个未完全初始化的Student 对象,直到您可以使用用户提供的信息对其进行更新。一般来说,这是一个非常糟糕的主意,因为仅仅有一个未完全初始化的对象的可能性(例如,在这种情况下缺少正确的 id 值)会使使用该对象的代码更加复杂,因为它必须检查,例如,是否存在是一个合适的 id 值。正确使用的复杂性,加上未能认识到正确使用所需的复杂性,会吸引疯狂的错误 - 不好。

    这就是为什么扩展 C 的 C++ 在分配和初始化之间提供了一种非常强的耦合。使用 C++ new 表达式,您将获得 both 成功的分配和成功的完整初始化,或者两者都没有(它会在失败时清除)。这就是问题应该更好地教导的内容!

    因此,我将教你可接受的 C++ 实践,而不是上面引用的给定问题(尽管通常要避免使用 new),这意味着回答这个修改后的问题:

    提示用户输入学生的名字、姓氏和 A - 编号(ID 号),然后使用这些值动态分配 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 表达式应该在理想情况下执行,即使某些事情失败并抛出异常。因此 trycatch

    但是,这样编码容易出错且冗长。

    相反,在更惯用的 C++ 编程中,人们将使用 智能指针,它是一个持有指针并提供指针操作的对象(因此看起来它一个指针),并且当不再使用指针时,其析构函数会自动执行delete 表达式。 C++ 标准库有几个这样的智能指针类。作为一般规则,尽可能使用限制性最强的智能指针,因为它的开销最小,并且很可能支持转换为更通用的智能指针,而相反的可能性则小得多,完全不可能。

    所以在这种情况下,您可以使用例如C++11 std::unique_ptr 或者如果你的编译器是旧的,C++03 std::auto_ptr,都来自&lt;memory&gt; 标头:

    // 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++ 非常依赖于复制,并且仍然可以生成非常快的程序!

    当然,在底层有很多肮脏的技巧来避免复制实际上发生在机器代码中。

    【讨论】:

    • @anonymous downvoter:请解释您的反对意见,以便其他人可以从您的见解中受益,而不会被您发现的错误误导。
    • @Alf:我投了反对票,我认为这是一个错误。不完全确定为什么,但如果你可以编辑你的答案,我就可以撤销反对票。
    • @Cheersandhth.-Alf:哇,这是一个答案,我喜欢它完成任务的方式,但仍然不能作为家庭作业的答案。不是我见过的最好的,但很好,最好的是“这应该适用于 x86”int main(){asm {...}}"
    【解决方案4】:

    new 返回一个指向对象的指针......所以你想要

    struct Student* student1 = new Student;
    

    【讨论】:

      【解决方案5】:

      struct 位于它定义的结构的名称之前。 :)

      您在尝试new Student 时看到的错误是什么?为什么它不起作用?

      【讨论】:

        【解决方案6】:

        new 语句返回指向newed 实例的指针。所以你需要将student1定义为一个指针。

        struct Student * student1 = new Student;
        

        【讨论】:

          【解决方案7】:

          您为什么使用new?只需声明一个变量的实例:

          Student student1;
          

          鉴于Student的定义,它看起来不像有身份, 它当然是可复制的,所以你可能永远不应该new它。

          (我也会为它提供一个构造函数,以便它可以被初始化 从定义的那一刻起就正确。)

          【讨论】:

            【解决方案8】:

            如果您想了解语法的概念或想感受语法.. 请始终处理此struct student = new student 语法。 as struct student = new sizeof(student);意味着您只是简单地将数据类型放入 sizeof()-fn for-size-confirmation。

            【讨论】:

              猜你喜欢
              • 2014-11-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多