成员变量
每个类可以没有成员,也可以定义多个成员,成员可以是数据、函数或类型别名。
一个类可以包含若干公有的、私有的和受保护的部分。在 public 部分定义的成员可被使用该类型的所有代码访问;在 private 部分定义的成员可被其他类成员访问。protected可以被子类访问。
?
构造函数
与类同名,且没有返回值的函数,用于构造一个对象。
一般使用一个初始化列表来初始化数据成员,如下:
Sales_item() : units_sold(0),revenue(0.0){}
?
成员函数
在类内部,声明成员函数是必需的,而定义成员函数则是可选的。
在类内部定义的函数默认为 inline。
在类外部定义的成员函数必须指明它们是在类的作用域中。Sales_item::avg_price 的定义使用作用域操作符来指明这是Sales_item 类中 avg_price 函数的定义。
将const加加到形参表之后,就可以变为常成员函数,在该函数内不能修改成员变量的值,如: double avg_price() const;
const必须出现在声明和定义中,若出现一处,就会出现一个编译时错误。
ps:const成员函数可以区分重载函数
?
? 显示指定inline成员函数
可以在类定义体内部指定一个成员为 inline,作为其声明的一部分。或者,也可以在类定义外部的函数定义上指定 inline。在声明和定义处指定 inline都是合法的。在类的外部定义 inline 的一个好处是可以使得类比较容易阅读。像其他 inline 一样,inline 成员函数的定义必须在调用该函数的每个源文件中是可见的。不在类定义体内定义的inline成员函数,其定义通常应放在有类定义的同一头文件中。
?
? 定义一个类
class Test{
}; //注意分号不能少
声明一个类
class Test; //叫前向声明
?
? 因为只有当类定义体完成后才能定义类,因此类不能具有自身类型的数据成员。然而,只要类名一出现就可以认为该类已声明。因此,类的数据成员可以是指向自身类型的指针或引用,如下:
?
class LinkScreen {
Screen window;
LinkScreen *next;
LinkScreen *prev;
};
?
? 类的定义分号结束。分号是必需的,因为在类定义之后可以接一个对象定义列表。定义必须以分号结束:
class Sales_item{ /* ... */ };
class Sales_item{ /* ... */ } accum, trans;
?
? 隐含的this指针
成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含形参命名为 this,与调用成员函数的对象绑定在一起。成员函数不能定义this 形参,而是由编译器隐含地定义。成员函数的函数体可以显式使用 this 指针,但不是必须这么做(比如return *this;)。如果对类成员的引用没有限定,编译器会将这种引用处理成通过 this 指针的引用。
?
? const 成员函数返回*this
在普通的非 const 成员函数中,this 的类型是一个指向类类型的 const指针。可以改变 this 所指向的值,但不能改变 this 所保存的地址。在 const 成员函数中,this 的类型是一个指向 const 类类型对象的const 指针。 既不能改变 this 所指向的对象, 也不能改变 this 所保存的地址。
****不能从 const 成员函数返回指向类对象的普通引用。const 成员函数只能返回 *this作为一个 const 引用。****
?
? 基于const的重载
基于成员函数是否为 const, 可以重载一个成员函数;同样地,基于一个指针或者引用形参是否指向 const,可以重载一个函数。const对象只能使用 const 成员。非 const 对象可以使用任一成员,但非 const 版本是一个更好的匹配。
?
? 可变数据成员
可变数据成员(mutable data member)永远都不能为 const(冲突),甚至当它是const 对象的成员时也如此。因此,const 成员函数可以改变mutable 成员。要将数据成员声明为可变的,必须将关键字 mutable 放在成员声明之前:
mutableint num;
?
? 形参表和函数体处于类作用域中
在定义于类外部的成员函数中,形参表和成员函数体都出现在成员名之后。这些都是在类作用域中定义,所以可以不用限定而引用其他成员。
charScreen::get(index r, indexc) const
{
index row = r * width; // compute the row location
return contents[row + c]; // offset by c to fetch specifiedcharacter
}
该函数用 Screen 内定义的 index 类型来指定其形参类型。因为形参表是在 Screen 类的作用域内,所以不必指明我们想要的是 Screen::index。我们想要的是定义在当前类作用域中的,这是隐含的。同样,使用 index、width 和contents 时指的都是 Screen 类中声明的名字。
? 函数返回类型不一定在类作用域中
与形参类型相比,返回类型出现在成员名字前面。如果函数在类定义体之外定义,则用于返回类型的名字在类作用域之外。如果返回类型使用由类定义的类型,则必须使用完全限定名。例如,考虑 get_cursor 函数:
classScreen {
public:
typedef std::string::size_type index;
index get_cursor() const;
};
inline Screen::index Screen::get_cursor() const
{
return cursor;
}
?
?
? 构造函数
Sales_items(); 对象在栈区
Sales_item*p=new Sales_item(); 对象在堆区
?
ps:构造函数不能声明为const。
Sales_item()const; //error
pps:与其他函数相同的是,构造函数具有名字(与类名相同),形参表,函数体,不同的是无返回值,返回类型,且包含一个构造函数初始化列表:
Sales_item::Sales_item(conststring &book):isbn(book), units_sold(0), revenue(0.0) { }
等价于
Sales_item::Sales_item(conststring &book)
{
isbn = book;
units_sold = 0;
revenue = 0.0;
}
注意:这个构造函数给类 Sales_item 的成员赋值,但没有进行显式初始化。不管是否有显式的初始化式,在执行构造函数之前,要初始化 isbn 成员。这个构造函数隐式使用默认的 string 构造函数来初始化 isbn。执行构造函数的函数体时,isbn成员已经有值了。该值被构造函数函数体中的赋值所覆盖。
从概念上讲,可以认为构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。计算阶段由构造函数函数体中的所有语句组成。
不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。初始化发生在计算阶段开始之前。在构造函数初