"动态分配一个学生,然后提示用户输入学生的名字、姓氏和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 表达式应该在理想情况下执行,即使某些事情失败并抛出异常。因此 try 和 catch。
但是,这样编码容易出错且冗长。
相反,在更惯用的 C++ 编程中,人们将使用 智能指针,它是一个持有指针并提供指针操作的对象(因此看起来它是一个指针),并且当不再使用指针时,其析构函数会自动执行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++ 非常依赖于复制,并且仍然可以生成非常快的程序!
当然,在底层有很多肮脏的技巧来避免复制实际上发生在机器代码中。