“C++ 完整指南”一书第 252 页上的错字?
a typo on page 252 of the book " a complete guide to c++"?
我正在看书 "a complete guide to c++"。我认为第 252 页上有错字。所以我有以下三个文件。
在文件 account.h、
中
// account.h
// Defining the class Account. class definition (methods prototypes) is usually put in the header file
// ---------------------------------------------------
#ifndef _ACCOUNT_ // if _ACCOUNT_ is not defined
#define _ACCOUNT_
#include <iostream>
#include <string>
using namespace std;
class Account
{
private:
string name;
unsigned long nr;
double balance;
public: //Public interface:
bool init( const string&, unsigned long, double);
void display();
};
#endif
// _ACCOUNT_
在文件 account.cpp、
中
// account.cpp
// Defines methods init() and display().
// ---------------------------------------------------
#include "account.h" // Class definition
#include <iostream>
#include <iomanip>
using namespace std;
// The method init() copies the given arguments
// into the private members of the class.
bool Account::init(const string& i_name,
unsigned long i_nr,
double i_balance)
{
if( i_name.size() < 1)
return false; // check data format to make sure it is valid
name = i_name;
nr = i_nr;
balance = i_balance;
return true;
}
// the method display() outputs private data.
void Account::display()
{
cout << fixed << setprecision(2)
<< "--------------------------------------\n"
<< "Account holder:" << name << '\n'
<< "Account number:" << nr << '\n'
<< "Account balance:" << balance << '\n'
<< "--------------------------------------\n"
<< endl;
}
最后,在文件中 account_t.cpp
// account_t.cpp
// Uses objects of class Account.
// ---------------------------------------------------
#include "account.h" // header file which contains class definition; (prototype for member functions)
int main()
{
Account current1, current2; // create two instances with name current1, current2
current1.init("Cheers, Mary", 1234567, -1200.99);
// have to call the init function to initialize a Account object; init function is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition
current1.display();
// current1.balance += 100; // Error: private member
current2 = current1;
current2.display();
current2.init("Jones, Tom", 3512347, 199.40);
current2.display();
Account& mtr = current1; // create a reference, which points to object current1
mtr.display();
return 0;
}
我认为不正确;因为显然没有办法访问 init 成员方法和 display 成员方法,对吗?我希望这不是一个天真的问题。
EDIT:我尝试 运行 文件 account_t.cpp 中的主要函数,并得到以下输出。
~$ g++ account_t.cpp
/tmp/ccSWLo5v.o: In function `main':
account_t.cpp:(.text+0x8c): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0xb6): undefined reference to `Account::display()'
account_t.cpp:(.text+0xd5): undefined reference to `Account::display()'
account_t.cpp:(.text+0x132): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0x15c): undefined reference to `Account::display()'
account_t.cpp:(.text+0x176): undefined reference to `Account::display()'
collect2: error: ld returned 1 exit status
非常感谢任何更多评论。
你问的问题没有问题。 init
和 display
在 class Account
的定义中被声明为 public
,具有 class 定义的文件在 #include
中被正确地编辑 .cpp
使用这些方法。
为了使用一个函数,只需要它的声明(在 class 中的 class 定义中,它包含在 header 中。 .cpp
文件中的 definition/implementation 不需要使用该函数。
每个 .cpp
都是单独编译的(称为 翻译单元 ),然后每次使用函数(可能只有声明可用)是 link 如有必要,通过函数的范围、名称和签名从其他翻译单元编辑为正确的定义。这称为 linking 过程。
一本 C++ 入门书应该解释编译和 linking 过程是如何工作的。
对于 g++
,有一种简单的方法可以将所有 .cpp
个文件单独编译为翻译单元,然后直接 link 将它们一起编译:
g++ account_t.cpp account.cpp
您总是需要像那样将所有 .cpp
添加到编译器调用中。 (还有其他方法,但这是最简单的方法。)
但是,如评论中所述,此程序还存在其他问题:
_ACCOUNT_
是一个保留标识符,程序中不能 #define
。所有以下划线开头后跟大写字母的标识符都是保留的。使用它们会导致未定义的行为。
using namespace std;
不好,至少在 header 文件中使用时,请参阅 Why is "using namespace std;" considered bad practice?.
类 有构造函数。大多数情况下不应该写 init
方法。构造函数负责构造和初始化 class 个实例。但是代码中没有使用构造函数。
钱永远不应该存储在 double
中,因为 double
的算术是不精确的。您应该将值存储在最小相关货币单位(例如美分)的整数类型中。
#include <iostream>
在 header 文件中不需要。应避免添加不需要的 #include
。
init
returns 表示初始化成功的布尔值。但是 main
从不检查该值。如果函数可能因错误值而失败,那么您必须检查函数返回的错误值以确保继续执行程序的其余部分是安全的。
其中一些要点作为初学者程序的简化可能是情有可原的,具体取决于这本书在这一点上的进展(尽管 200 多页应该已经涵盖了很多),但其他则不然。
构造函数使用做同样事情的例子:
class Account
{
private:
string name;
unsigned long nr;
double balance;
public: //Public interface:
Account(const string&, unsigned long, double);
void display();
};
Account::Account(const string& i_name,
unsigned long i_nr,
double i_balance)
: name(i_name), nr(i_nr), balance(i_balance)
{
}
int main()
{
Account current1("Cheers, Mary", 1234567, -1200.99);
// Create Account instance and initialize private members; constructor is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition
current1.display();
// current1.balance += 100; // Error: private member
Account current2 = current1; // Create second Account instance and copy private members from first one
current2.display();
current2 = Account("Jones, Tom", 3512347, 199.40); // Replace instance with a copy of a new one
current2.display();
Account& mtr = current1; // create a reference, which points to object current1
mtr.display();
return 0;
}
i_name.size() < 1
检查(写得很奇怪,为什么不 i_name.size() == 0
?)将通过从构造函数中抛出异常来实现:
Account::Account(const string& i_name,
unsigned long i_nr,
double i_balance)
: name(i_name), nr(i_nr), balance(i_balance)
{
if(i_name.size() == 0) {
throw invalid_argument("Account does not accept empty names!");
}
}
这需要 #include<stdexcept>
并且是一个更高级的主题。
我正在看书 "a complete guide to c++"。我认为第 252 页上有错字。所以我有以下三个文件。
在文件 account.h、
中// account.h
// Defining the class Account. class definition (methods prototypes) is usually put in the header file
// ---------------------------------------------------
#ifndef _ACCOUNT_ // if _ACCOUNT_ is not defined
#define _ACCOUNT_
#include <iostream>
#include <string>
using namespace std;
class Account
{
private:
string name;
unsigned long nr;
double balance;
public: //Public interface:
bool init( const string&, unsigned long, double);
void display();
};
#endif
// _ACCOUNT_
在文件 account.cpp、
中// account.cpp
// Defines methods init() and display().
// ---------------------------------------------------
#include "account.h" // Class definition
#include <iostream>
#include <iomanip>
using namespace std;
// The method init() copies the given arguments
// into the private members of the class.
bool Account::init(const string& i_name,
unsigned long i_nr,
double i_balance)
{
if( i_name.size() < 1)
return false; // check data format to make sure it is valid
name = i_name;
nr = i_nr;
balance = i_balance;
return true;
}
// the method display() outputs private data.
void Account::display()
{
cout << fixed << setprecision(2)
<< "--------------------------------------\n"
<< "Account holder:" << name << '\n'
<< "Account number:" << nr << '\n'
<< "Account balance:" << balance << '\n'
<< "--------------------------------------\n"
<< endl;
}
最后,在文件中 account_t.cpp
// account_t.cpp
// Uses objects of class Account.
// ---------------------------------------------------
#include "account.h" // header file which contains class definition; (prototype for member functions)
int main()
{
Account current1, current2; // create two instances with name current1, current2
current1.init("Cheers, Mary", 1234567, -1200.99);
// have to call the init function to initialize a Account object; init function is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition
current1.display();
// current1.balance += 100; // Error: private member
current2 = current1;
current2.display();
current2.init("Jones, Tom", 3512347, 199.40);
current2.display();
Account& mtr = current1; // create a reference, which points to object current1
mtr.display();
return 0;
}
我认为不正确;因为显然没有办法访问 init 成员方法和 display 成员方法,对吗?我希望这不是一个天真的问题。
EDIT:我尝试 运行 文件 account_t.cpp 中的主要函数,并得到以下输出。
~$ g++ account_t.cpp
/tmp/ccSWLo5v.o: In function `main':
account_t.cpp:(.text+0x8c): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0xb6): undefined reference to `Account::display()'
account_t.cpp:(.text+0xd5): undefined reference to `Account::display()'
account_t.cpp:(.text+0x132): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0x15c): undefined reference to `Account::display()'
account_t.cpp:(.text+0x176): undefined reference to `Account::display()'
collect2: error: ld returned 1 exit status
非常感谢任何更多评论。
你问的问题没有问题。 init
和 display
在 class Account
的定义中被声明为 public
,具有 class 定义的文件在 #include
中被正确地编辑 .cpp
使用这些方法。
为了使用一个函数,只需要它的声明(在 class 中的 class 定义中,它包含在 header 中。 .cpp
文件中的 definition/implementation 不需要使用该函数。
每个 .cpp
都是单独编译的(称为 翻译单元 ),然后每次使用函数(可能只有声明可用)是 link 如有必要,通过函数的范围、名称和签名从其他翻译单元编辑为正确的定义。这称为 linking 过程。
一本 C++ 入门书应该解释编译和 linking 过程是如何工作的。
对于 g++
,有一种简单的方法可以将所有 .cpp
个文件单独编译为翻译单元,然后直接 link 将它们一起编译:
g++ account_t.cpp account.cpp
您总是需要像那样将所有 .cpp
添加到编译器调用中。 (还有其他方法,但这是最简单的方法。)
但是,如评论中所述,此程序还存在其他问题:
_ACCOUNT_
是一个保留标识符,程序中不能#define
。所有以下划线开头后跟大写字母的标识符都是保留的。使用它们会导致未定义的行为。using namespace std;
不好,至少在 header 文件中使用时,请参阅 Why is "using namespace std;" considered bad practice?.类 有构造函数。大多数情况下不应该写
init
方法。构造函数负责构造和初始化 class 个实例。但是代码中没有使用构造函数。钱永远不应该存储在
double
中,因为double
的算术是不精确的。您应该将值存储在最小相关货币单位(例如美分)的整数类型中。#include <iostream>
在 header 文件中不需要。应避免添加不需要的#include
。init
returns 表示初始化成功的布尔值。但是main
从不检查该值。如果函数可能因错误值而失败,那么您必须检查函数返回的错误值以确保继续执行程序的其余部分是安全的。
其中一些要点作为初学者程序的简化可能是情有可原的,具体取决于这本书在这一点上的进展(尽管 200 多页应该已经涵盖了很多),但其他则不然。
构造函数使用做同样事情的例子:
class Account
{
private:
string name;
unsigned long nr;
double balance;
public: //Public interface:
Account(const string&, unsigned long, double);
void display();
};
Account::Account(const string& i_name,
unsigned long i_nr,
double i_balance)
: name(i_name), nr(i_nr), balance(i_balance)
{
}
int main()
{
Account current1("Cheers, Mary", 1234567, -1200.99);
// Create Account instance and initialize private members; constructor is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition
current1.display();
// current1.balance += 100; // Error: private member
Account current2 = current1; // Create second Account instance and copy private members from first one
current2.display();
current2 = Account("Jones, Tom", 3512347, 199.40); // Replace instance with a copy of a new one
current2.display();
Account& mtr = current1; // create a reference, which points to object current1
mtr.display();
return 0;
}
i_name.size() < 1
检查(写得很奇怪,为什么不 i_name.size() == 0
?)将通过从构造函数中抛出异常来实现:
Account::Account(const string& i_name,
unsigned long i_nr,
double i_balance)
: name(i_name), nr(i_nr), balance(i_balance)
{
if(i_name.size() == 0) {
throw invalid_argument("Account does not accept empty names!");
}
}
这需要 #include<stdexcept>
并且是一个更高级的主题。