定义成员函数
成员函数的声明必须在类的内部,它的定义既可以在类的内部也可以在类的外部。
struct Sales_data
{
std::string isbn() const { return bookNo; }
double arv_price() const;
};
- isbn函数的定义和声明都在类的内部
- arv_price函数只在类的内部做了声明,需要在其它地方进行实现
定义在类的内部的函数是隐式的inline函数
this
total.isbn();
我们使用点运算符来访问total对象的isbn成员,然后调用它
当isbn返回bookNo时,实际上它隐式地返回total.bookNo
那么成员函数是如何访问调用它的对象的呢?
类的成员函数通过一个名为this的额外的隐式参数来访问调用它的那个对象
当我们调用一个成员函数时,用请求该函数的对象地址初始化this
这个操作是隐式进行的,this形参也是隐式的
this是一个常量指针
const成员函数
this指针虽然是隐式定义的,但是它也需要遵循初始化规则。
默认情况下,this是指向类的非常量对象的常量指针。
因此,我们无法将类的常量对象的地址赋值给this指针(无法避免通过this修改对象成员的值)。这和普通指针的赋值规则是一致的。
在isbn的实现中,它也只是读取了数据成员的值,并未对成员进行任何修改。因此它是可以被类的常量对象调用的。
基于以上原因,如果将隐式的this形参声明为指向常量对象的常量指针,会大大提高isbn函数的适用范围。(c++允许将非常量赋值给指向常量的指针)
但是this是隐式的,因此通过在参数列表后添加const关键字来完成这个声明。
一旦给成员函数添加了const声明,则在函数体内只能访问成员,不能修改成员的值。
如果成员被声明成常量成员函数,那么它的定义也必须在参数列表后明确指定const属性
double Saled_data::avg_price() const
{
return units_sold;
}
这里的const不能省略
在类外定义成员函数时,带上类名和作用域运算符是很有必要的,一旦编译器看到这个函数名,就能理解剩余的代码是位于类的作用域内
返回this对象
Sales_data& Sales_data::combine(const Sales_data &rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
该函数是在模仿运算符+=,而内置的赋值运算符把它的左侧运算对象当成左值返回,为了和它保持一致,combine函数返回引用类型
total.combine(trans);
返回的是total的引用
定义类相关的非成员函数
类的设计者常常需要定义一些辅助函数,尽管这些函数定义的操作从概念上来说属于类的接口的一部分,但它们实际并不属于类本身。
这里需要注意的是,这些非成员函数出现的位置
struct Sales_data
{
};
Sales_data add(const Sales_data&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
构造函数
- 每个类都分别定义了它的对象被初始化的方式
- 由一个或多个构造函数来控制对象的初始化过程
- 无论何时只要类的对象被创建,就会执行构造函数
构造函数是一种特殊的成员函数
- 它的名字和类名相同
- 没有返回类型
- 不能被声明成const的
当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其”常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。
形如,
Sales_data total;
Sales_data trans;
的对象定义过程,我们没有在定义对象时为其提供初始值,它们执行的是默认初始化。
类通过一个特殊的构造函数来控制默认初始化的过程,称为默认构造函数
默认构造函数无须任何实参
如果我们的类没有显式地定义构造函数,编译器就会为我们隐式地定义一个默认构造函数。
由编译器创建的构造函数称为合成的构造函数。
在下列情况下,最好能提供默认构造函数
- 只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数。因此,如果我们为类定义了一些其他构造函数,最好也能提供默认构造函数,否则类没有默认构造函数。
- 合成的默认构造函数可能执行错误的操作。比如默认初始化数据成员时,可能得到未定义的值
- 如果类中包含其它类类型的成员,且这个成员的类型没有默认构造函数,我们必须自定义默认构造函数,否则该类没有可用的默认构造函数。
strcut Sales_data
{
Sales_data() = default;
Sales_data(const std::string &s):bookNo(s){}
};
=default
这里我们定义了其他构造函数,因此编译器不会为我们生成合成的默认构造函数。
所以自定义了一个默认构造函数,且在它的后面添加了=default关键字
=default要求编译器生成默认构造函数,使用和合成的默认构造函数一样的规则来初始化成员。
=default可以和声明一起出现,也可以和定义一起出现在类的外部。
如果出现在类的内部,则默认构造函数是内联的。
构造函数初始值列表
Sales_data(const std::string &s):bookNo(s){}
:bookNo(s)这部分函数称为构造函数初始值列表,它的作用是使用构造函数的实参完成成员的初始化过程。
没有出现在初始值列表中的成员,也有三个机会完成初始化
- 构造函数的函数体
- 类内初始值
- 默认初始化