第七章 函数(part5) 类的成员函数
来源:互联网 发布:水乳推荐知乎 编辑:程序博客网 时间:2024/06/05 00:41
7.7. 类的成员函数
成员函数的定义与普通函数的定义类似。和任何函数一样,成员函数也包含下面四个部分:
函数返回类型。
函数名。
用逗号隔开的形参表(也可能是空的)。
包含在一对花括号里面的函数体。
正如我们知道的,前面三部分组成函数原型。函数原型定义了所有和函数相关的类型信息:函数返回类型是什么、函数的名字、应该给这个函数传递什么类型的实参。
函数原型必须在类中定义。但是,函数体则既可以在类中也可以在类外定义。
知道这些后,观察下面扩展的类定义,我们为这个类增加了两个新成员:成员函数 avg_price 和 same_isbn。其中 avg_price 函数的形参表是空的,返回 double 类型的值。
而 same_isbn 函数则返回 bool 对象,有一个 const Sales_item 类型的引用形参。
class Sales_item { public: // operations on Sales_item objects double avg_price() const; bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; } // private members as before private: std::string isbn; unsigned units_sold; double revenue; };在解释跟在形参表后面的 const 之前,必须先说明成员函数是如何定义的。
7.7.1. 定义成员函数的函数体
类的所有成员都必须在类定义的花括号里面声明,此后,就不能再为类增加任何成员。类的成员函数必须如声明的一般定义。
类的成员函数既可以在类的定义内也可以在类的定义外定义。在类 Sales_item 中,这两种情况各有一例说明:函数same_isbn 在类内定义,而函数avg_price 则在类内声明,在类外定义。
编译器隐式地将在类内定义的成员函数当作内联函数。再详细观察函数 same_isbn 的定义:
bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; }
与任何函数一样,该函数的函数体也是一个块。在这个函数中,块中只有一个语句,比较两个 Sales_item 对象的数据成员isbn 的值,并返回比较结果。
首先要注意的是:成员 isbn 是 private 的。尽管如此,上述语句却没有任何错误。
<Note>:类的成员函数可以访问该类的 private 成员。
成员函数含有额外的、隐含的形参
调用成员函数时,实际上是使用对象来调用的。例如,调用书店程序中的函数 same_isbn,是通过名为 total 的对象来执行 same_isbn 函数的:
if (total.same_isbn(trans))
在这个调用中,传递了对象 trans。作为执行调用的一部分,使用对象 trans 初始化形参rhs。于是,rhs.isbn 是trans.isbn 的引用。
而没有前缀的 isbn 使用了相同的实参绑定过程,使之与名为 total 的对象绑定起来。每个成员函数都有一个额外的、隐含的形参将该成员函数与调用该函数的类对象捆绑在一起。当调用名为total 的对象的same_isbn 时,这个对象也传递给了函数。而same_isbn 函数使用 isbn 时,就隐式地使用了调用该函数的对象的isbn 成员。
这个函数调用的效果是比较 total.isbn 和 trans.isbn 两个值。
每个成员函数(除了static 成员函数外)都有一个额外的、隐含的形参 this。在调用成员函数时,形参 this 初始化为调用函数的对象的地址。
为了理解成员函数的调用,可考虑下面的语句:
total.same_isbn(trans);
就如编译器这样重写这个函数调用:
// pseudo-code illustration of how a call to a member function is translated Sales_item::same_isbn(&total, trans);
在这个调用中,函数 same_isbn 中的数据成员 isbn 属于对象 total。const 成员函数的引入
现在,可以理解跟在 Sales_item 成员函数声明的形参表后面的 const 所起的作用了:const 改变了隐含的this 形参的类型。
在调用 total.same_isbn(trans) 时,隐含的 this 形参将是一个指向total 对象的const Sales_Item* 类型的指针。就像如下编写same_isbn 的函数体一样:
// pseudo-code illustration of how the implicit this pointer is used // This code is illegal: We may not explicitly define the this pointer ourselves // Note that this is a pointer to const because same_isbn is a const member bool Sales_item::same_isbn(const Sales_item *const this, const Sales_item &rhs) const { return (this->isbn == rhs.isbn); }用这种方式使用 const 的函数称为常量成员函数。由于this 是指向const 对象的指针,const 成员函数不能修改调用该函数的对象。
const 对象、指向 const 对象的指针或引用只能用于调用其 const 成员函数,如果尝试用它们来调用非 const 成员函数,则是错误的。
在成员函数中,不必显式地使用 this 指针来访问被调用函数所属对象的成员。对这个类的成员的任何没有前缀的引用,都被假定为通过指针this 实现的引用:
bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; }在这个函数中 isbn 的用法与 this->units_sold 或 this->revenue 的用法一样。
由于 this 指针是隐式定义的,因此不需要在函数的形参表中包含 this 指针,实际上,这样做也是非法的。但是,在函数体中可以显式地使用this 指针。如下定义函数same_isbn 尽管没有必要,但是却是合法的:
bool same_isbn(const Sales_item &rhs) const { return this->isbn == rhs.isbn; }
7.7.2. 在类外定义成员函数
在类的定义外面定义成员函数必须指明它们是类的成员:
double Sales_item::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; }
上述定义和其他函数一样:该函数返回类型为 double,在函数名后面的圆括号起了一个空的形参表。新的内容则包括跟在形参表后面的const 和函数名的形式。函数名:
Sales_item::avg_price使用作用域操作符指明函数 avg_price 是在类 Sales_item 的作用域范围内定义的。
现在可以完全理解第一行代码了:这行代码说明现在正在定义类 Sales_item 的函数 avg_price,而且这是一个const 成员函数,这个函数没有(显式的)形参,返回double 类型的值。
函数体更加容易理解:检查 units_sold 是否为 0,如果不为 0,返回 revenue 除以 units_sold 的结果;7.7.3. 编写 Sales_item 类的构造函数
还必须编写一个成员,那就是构造函数。正如在所学习的,在定义类时没有初始化它的数据成员,而是通过构造函数来初始化其数据成员。构造函数是特殊的成员函数
构造函数是特殊的成员函数,与其他成员函数不同,构造函数和类同名,而且没有返回类型。构造函数的形参指定了创建类类型对象时使用的初始化式。通常,这些初始化式会用于初始化新创建对象的数据成员。
构造函数通常应确保其每个数据成员都完成了初始化。
Sales_item 类只需要显式定义一个构造函数:没有形参的默认构造函数。默认构造函数说明当定义对象却没有为它提供(显式的)初始化式时应该怎么办:
vector<int> vi; // default constructor: empty vector string s; // default constructor: empty string Sales_item item; // default constructor: ???
我们知道 string 和 vector 类默认构造函数的行为:这些构造函数会将对象初始化为合理的默认状态。string 的默认构造函数会产生空字符串上,相当于""。vector 的默认构造函数则生成一个没有元素的vector 向量对象。
同样地,我们希望类 Sales_items 的默认构造函数为它生成一个空的 Sales_item 对象。这里的“空”意味着对象中的isbn 是空字符串,units_sold 和revenue 则初始化为 0。
构造函数的定义
和其他成员函数一样,构造函数也必须在类中声明,但是可以在类中或类外定义。由于我们的构造函数很简单,因此在类中定义它:
class Sales_item { public: // operations on Sales_item objects double avg_price() const; bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; } // default constructor needed to initialize members of built-in type Sales_item(): units_sold(0), revenue(0.0) { } // private members as before private: std::string isbn; unsigned units_sold; double revenue; };
在解释任何构造函数的定义之前,注意到构造函数是放在类的 public 部分的。通常构造函数会作为类的接口的一部分,这个例子也是这样。
毕竟,我们希望使用类 Sales_item 的代码可以定义和初始化类 Sales_item 的对象。如果将构造函数定义为private 的,则不能定义类Sales_item 的对象,这样的话,这个类就没有什么用了。
As to the definition itself
对于定义本身:
// default constructor needed to initialize members of built-in type Sales_item(): units_sold(0), revenue(0.0) { }
上述语句说明现在正在定义类 Sales_item 的构造函数,这个构造函数的形参表和函数体都为空。令人感兴趣的是冒号和冒号与定义(空)函数体的花括号之间的代码。
构造函数和初始化列表
在冒号和花括号之间的代码称为构造函数的初始化列表。构造函数的初始化列表为类的一个或多个数据成员指定初值。合成的默认构造函数
<Note>:如果没有为一个类显式定义任何构造函数,编译器将自动为这个类生成默认构造函数。
合成的默认构造函数一般适用于仅包含类类型成员的类。而对于含有内置类型或复合类型成员的类,则通常应该定义他们自己的默认构造函数初始化这些成员。
7.7.4. 类代码文件的组织
通常将类的声明放置在头文件中。大多数情况下,在类外定义的成员函数则置于源文件中。- 第七章 函数(part5) 类的成员函数
- 《C++ Primer》读书笔记第七章-3-构造函数再探 And 类的静态成员
- 第七周上机任务1--构建含有静态数据成员和成员函数的Time 类
- 第七周实验报告(一)含有静态数据成员和成员函数的TIME类
- 第七周任务1含有静态数据成员和成员函数的Time类
- 第七周任务一:含有静态数据成员和成员函数的Time类
- 第七周c++任务一。含有静态数据成员和成员函数的Time类
- part5:仿函数,仿函数类(一)
- 第七周2成员,友元,一般函数的区别
- 第七周项目 求两点之间的距离 成员函数
- 类的成员函数
- 类的成员函数
- 类的成员函数
- 类的成员函数
- 类的成员函数
- 类的成员函数
- 第七周项目1-点类-成员函数
- 第七周项目1-点类-成员函数
- A Perfect Free Download Manager Uninstall Guide
- bandbox的用法
- 如何将中缀式转化成前缀式和后缀式(波兰和逆波兰)(适合考试使用的简单办法)
- Python 切片运算符
- ios 720全景资料
- 第七章 函数(part5) 类的成员函数
- Redhat6.1 IP设置
- 汉字转拼音缩写
- 教你如何编写自己的bat文件
- BAT批处理文件判断文件并执行
- (一) 路由解析
- bat文件制作
- 正确读取 ANSI 编码的文本文件 UTF8
- TCP与UDP区别