目录:
- 类作用域
- 运算符重载
- 友元(友元函数、友元类、友元成员函数)
- 类的自动类型转换和强制类型转换
五、类作用域
- 可以在不同类中使用相同的类成员名。在类中定义的名称(如类数据成员名和类成员函数名)的作用域都为整个类,作用域为整个类的名称只在该类中是已知的。所以要调用公有成员函数,必须通过对象(定义成员函数时须使用域解析运算符)。
- 可以用枚举为整型常量提供作用域为整个类的符号名称
private: //这种方式声明枚举并不会创建类成员数据
enum {Months = 12};
double costs[Months];
3. C++另一种在类中定义常量的方式—关键字static
private:
static const int Months = 12; //这将创建Months常量,其与其他静态
double costs[Months]; //变量存储在一起,而不是存储在对象中
4. C++11提供了一种新枚举,其枚举量的作用域为类,如:
enum class egg {Small, Medium, Large};
enum class t_shirt{Small, Medium, Large};
其作用域内枚举的底层类型为int。
5. 一般来说,私有数据成员存储信息,公有成员函数(又称方法)提供访问数据的唯一途径。C++试图让用户定义的类型尽可能与标准类型类似,因此可以声明对象、指向对象的指针和对象数组。可以按值传递对象、将对象作为函数返回值、将一个对象赋给同类型的另一个对象。
如希望成员函数对多个对象进行操作,可以将额外的对象作为参数传递给它。
this指针被设置为调用对象的地址,*this是该对象的别名。
6. 类很适合用于描述ADT(抽象数据类型)。公有成员函数接口提供了ADT描述的服务,类的私有部分和类方法的代码提供了实现,这些实现对类的客户隐藏。
7. 定义函数时不要返回指向局部变量或临时对象(变量)的引用。函数执行完毕后,局部变量和临时变量将消失,引用将指向不存在的数据。
六、运算符重载
1. C++允许将运算符重载扩展到用户定义的类型。要重载运算符,需使用被称为运算符函数的特殊函数形式,格式如:
operator op(argument_list) //op必须时有效的C++运算符
例如:
class Time{
int hours;
int minutes;
public:
Time operator+(const Time & t) const
……
};
Time Time::operator+(const Time & t) const
{
Time sum;
sum.hours=hours+t.hours + … ;
……
return sum;
}
在测试程序中有两种表示法调用operator+( )方法:
Time coding(2,40);
Time fixing(5,55);
total = coding.operator+(fixing); //函数表示法
total = coding + fixing; //运算符表示法
在运算符表示法中,运算符左侧的对象(coding)是调用对象,运算符右边的对象(fixing)是作为参数被传递的对象。
2. 重载限制
(1) 重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。
(2)使用运算符时不能违反运算符原来的句法规则,不能修改运算符的优先级。
(3)不能创建新运算符。
(4)大多运算符都可通过成员或非成员函数进行重载,但赋值运算符‘=’、函数调用‘()’、下标‘[ ] ’、通过指针访问类成员‘->’运算符只能通过成员运算符进行重载。
七、友元(友元函数、友元类、友元成员函数)
- 友元函数的原型在类声明中,并在原型声明前加关键字friend。
friend Time operator * (double m, const Time & t);
ps: operator * ( )虽在类中声明,但它不是成员函数,因此不能使用成员运算符来调用,但它与成员函数的访问权限相同。
不要在定义函数时使用friend,类的友元函数不是成员函数,其访问权限与成员函数相同。
2. 应将友元函数看作类的扩展接口的组成部分。类方法和友元函数只是表达类接口的两种不同机制。如果要为类重载运算符,并将非类的项作为其第一个操作数,则可以使用友元函数来反转操作数的顺序。
3. 常用的友元:重载<<运算符
cout是一个ostream对象,能识别所有C++基本类型,这是因为对于每种基本类型,ostream类声明中都包含了相应的重载的operator<<( )定义。ostream类将operator<<( )函数实现返回一个指向ostream对象的引用。
cout<<x<<y; 等同于(cout<<x)<<y;
4. 只有在类声明中的原型才能使用friend关键字,除非函数定义也是原型,否则不能在函数定义中使用它。
5. 对于很多运算符来说,可选择使用成员函数或非成员函数来实现运算符重载。一般来说,非成员函数应是友元函数,这样它才能直接访问类的私有数据。非成员版本的重载运算符函数所需的形参数目与运算符使用的操作数数目相同;而成员版本所需的参数数目少一个,因为其中的一个操作数是被隐式地传递的调用对象。(this指针)
6. 状态成员:描述对象所处的状态。如果方法通过计算得到一个新的类对象,则应考虑是否可以使用类构造函数来完成。
7. 头文件cstdlib包含了srand( )和rand( )的原型,ctime包含了time( )的原型。
time(0)返回程序执行到当前的时间,以秒级。
srand( )函数允许覆盖默认的种子值,重新启动另一个随机数序列。使用语句srand(time(0))可使rand( )产生更随机的随机数(再次运行程序时结果会不一样)。C++使用头文件radom中函数提供更强大的随机数支持。
八、类的自动类型转换和强制类型转换
只接受一个参数的构造函数定义了从参数类型到类类型的转换。若使用关键字explicit限定了这种构造函数,则它只能用于显式转换,否则也可以用于隐式转换。
1. 转换函数
构造函数只用于从某种类型到类类型的转换;转换函数是用户定义的强制类型转换,可以像使用强制类型转换那样使用它们。
创建转换函数,转换为typeName类型:operator typeName( ); 注意:转换函数必须是类方法(通过类对象调用),不能指定返回类型也不能有参数。
例:
class Stonewt
{
……
double pounds;
public:
……
operator int( ) const;
};
Stonewt::operator int ( ) const
{
return int(pounds+0.5); //int转换将待转换的值四舍五入为最接近的整数,
} //如114.4(+0.5)->114.9->114
int main()
{
…… //定义类对象c中pounds为128.8
cout<<int(c)<<endl; //输出为129
}
原则上最好使用显式转换,避免隐式转换
2. 总之,C++为类提供了下面的类型转换
(1)只有一个参数的类构造函数用于将类型与该参数相同的值转换为类类型。例如,将int值赋给Stonewt对象时,接受int参数的Stonewt类构造函数将自动被调用。在构造函数声明中使用explicit可防止隐式转换,而只允许显式转换。
(2)被称为转换函数的特殊类成员运算符函数,用于将类对象转换为其它类型。转换函数是类成员,没有返回类型,没有参数,名为operator typeName( ),其中typeName是对象将被转换的类型。将类对象赋给typeName变量或将其强制转换为typeName类型时,该转换函数将自动被调用。