动态分配一个学生对象,然后提示用户输入学生的名字、姓氏和A号(ID号)。这个任务要求你在更新学生信息之前拥有一个不完全初始化的Student对象。一般来说,这是非常糟糕的想法,因为即使只有可能存在一个未完全初始化的对象(例如,在这种情况下缺少适当的id值),使用该对象的代码也会变得更加复杂,因为它必须检查是否存在适当的id值。这种复杂性对于正确使用来说是必要的,而忽视这种复杂性会引发许多错误-不好的。
这就是为什么C++在扩展C时提供了非常强的耦合性,即在分配和初始化之间。使用C++的new表达式,您可以获得成功的分配和成功的完全初始化,否则两者都不会成功(如果失败会进行清理)。这才是问题应该更好地教授的内容!
因此,与其引用上面给出的问题,我将教您可接受的C++实践方法(尽管通常应避免使用new),这意味着回答以下修改后的问题:
提示用户输入学生的名字、姓氏和A号(ID号),然后动态分配一个具有这些值的Student对象。
好的,下面开始:
#include <assert.h>
#include <iostream>
#include <string>
#include <sstream>
#include <stdexcept>
#include <stdlib.h>
#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 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 );
}
}
void cppMain()
{
using namespace blah;
Student const* const pStudent = studentFromInput();
try
{
cout
<< "The student is "
<< pStudent->firstName << " " << pStudent->lastName
<< ", with id " << pStudent->id << "."
<< endl;
delete pStudent;
}
catch( std::exception const& )
{
delete pStudent;
throw;
}
}
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>
头文件中:
#include <assert.h>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include <stdexcept>
#include <stdlib.h>
#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 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 ) );
}
}
void cppMain()
{
using namespace blah;
unique_ptr<Student> const pStudent = studentFromInput();
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++非常依赖于复制,并且仍然产生相当快的程序!
当然,在底层有许多肮脏的技巧,以避免在机器代码中实际发生复制。
unique_ptr
所做的事情。除了它会为你完成这些操作。不要使用裸指针,它们很糟糕。 - Etienne de Martelstudent.firstName
,但它显示表达式必须具有类类型。虽然我以为我用Student* student = new Student;
进行了声明。 - sircrisp->
通过指针访问成员。 - R. Martinho Fernandes