1.类作用域和类定义作用域
类定义作用域为包含类定义的花括号语句块,如果没有花括号语句块,则为从类定义开始的全部代码空间。
类作用域仅包括类定义内部和所有其成员函数的定义体。
#include <iostream>
using namespace std;
class X{
public:
void f1(){
m = 6;
f2();
}
void f2();
private:
int m;
};
void X::f2()
{
cout<<"Data member: "<<m<<endl; // X::m
int m = 7;
cout<<"Local member: "<<m<<endl; //X::m被隐藏
cout<<"Data member: "<<X::m<<endl;
}
int main() //此处以下不属于类作用域但属于类定义作用域
{
X x;
x.f1();
}
上述代码中的成员函数f1访问了类定义中稍后声明的私有数据m(第16行)和成员函数f2(第14行),这是f1的权利。即使在类定义的外部,其成员函数f2(第18~23行)也可以访问私有数据m,只是敌不过函数体中的局部同名数据m,而遭到隐藏的冷遇,但通过类名空间的::操作,一样可以访问。
2.静态成员
静态数据成员:每个类只有一个静态数据成员实体,每个对象不再有它的副本。
#include <iostream>
#include <string> //由于cout不支持string类型的数据,所以头文件中应该包括string
using namespace std;
class Student{
static int number;
string name;
public:
Student(string str){
name = str;
number++;
}
void print() { cout<<name<<"->students are "<<number<<" numbers\n"; }
};
int Student::number = 0; //静态数据成员在类外分配空间和初始化
void fn()
{
Student s1("Smith");
Student s2("Randy");
s1.print();
}
int main()
{
Student s("Smith");
fn();
s.print();
};
整个类中只有一份number拷贝,所有的对象都共享这份拷贝。因此,输出学生总数时,访问的是唯一的静态数据成员,它不会因对象而异了。
定义静态成员的格式不能重复static关键字(第18行),但必须在成员名前冠以类名加域操作符,以表示该成员的类属。如果不将其初始化,则系统将为该成员清0。
3.静态成员函数
将静态成员做成私有的,用静态成员函数去访问静态数据成员是合适的。
#include <iostream>
#include <string> //由于cout不支持string类型的数据,所以头文件中应该包括string
using namespace std;
class Student{
static int number;
string name;
public:
Student(string str){
name = str;
number++;
}
static void printNumber(){ cout<<number<<" total numbers\n"; }
void print() { cout<<name<<"->students are "<<number<<" numbers\n"; }
};
int Student::number = 0; //静态数据成员在类外分配空间和初始化
void fn()
{
Student s1("Smith");
Student s2("Randy");
s1.print();
s1.printNumber();
}
int main()
{
Student s("Jenny");
fn();
s.print();
Student::printNumber();
};
静态成员函数并不受对象的牵制,可以用对象名调用静态成员函数,也可以用类名加上域操作符调用静态成员函数,这时候,将它看作是某个名空间的一个函数。
静态成员函数的实现位置与成员函数的实现位置应该是一起的,静态成员函数如果不在类中实现,而在类的外部实现时,类名前应免去static关键字。成员函数的静态性只在类中声明的时候才是必要的。
因为静态成员函数可以不以捆绑对象的形式调用,静态成员函数被调用时,没有当前对象的信息,所以静态成员函数不能访问数据成员,如下:
static void printNumber(){ cout<<name<<number<<" total numbers\n"; }
则编译通不过,error: illegal reference to non-static member 'Student::name。这并不是说它没有访问私有数据访问的权限,如果在静态成员函数中,给它传递一个Student对象:
static void printNumber(Student& s){
cout<<"My name is "<<s.name<<"\n";
cout<<s.number<<" total number\n";
}
那么,访问私有数据便是它的权利。
4.关于增量操作符的重载
一个整型变量的前增量操作的结果与变量值是一致的,而且前增量操作的结果是左值,操作可以连贯。而后增量操作的结果是增量之前的变量值,它是临时变量,当表达式计算工作完成后,该临时变量随即消失,所以变量最终值与后增量结果是错位的。例如:
int a=1, b=1, c=1, d=1;
(++a)++; //结果a=3
(b++)++; //结果b=2,(b++)的结果是临时变量,在其上加1随后又抛弃
++(++c); //结果c=3
++(d++); //结果d=2,与b相似
在反映对象的前增量操作时,要求参数为对象的引用,返回的仍然是该对象参数的引用:
X& operator++(X& a); //前增量操作符
++a; //等价于operator++(a); 匹配上述操作符声明
后增量操作符的重载,同样要求参数为对象的引用,因为在调用的上下文中,实参将发生变化,而返回则为临时对象,所以为非引用的对象值。在区分前后增量操作符时,C++做了一个技术处理:
X operator++(X& a, int b); //后增量操作符
a++; //等价于operator++(a,1); 匹配上述操作符声明
调用后增量操作符的参数匹配是违背函数参数匹配常规的,编译专门做了特殊处理。
#include <iostream>
#include <iomanip>
using namespace std;
class Time
{
int hour, minute, second;
public:
Time (int h , int m , int s ) : hour(h), minute(m), second(s) {}
friend Time& operator++(Time& a);
friend Time operator++(Time& a, int);
friend ostream& operator<<(ostream& o, const Time& t);
};
Time& operator++(Time& a) //友元函数可以直接访问对象的私有成员,但是友元的函数参数中必须有对象才行!前增量操作符
{
if (!(a.second = (a.second+1)%60) && !(a.minute = (a.minute+1) % 60))
a.hour = (a.hour+1)%24;
return a;
}
Time operator++(Time& a, int) //后增量操作符
{
Time t(a);
if (!(a.second = (a.second+1)%60) && !(a.minute = (a.minute+1) % 60))
a.hour = (a.hour+1)%24;
return t;
}
ostream& operator<<(ostream& o, const Time& t)
{
o<<setfill('0')<<setw(2)<<t.hour<<":"<<setw(2)<<t.minute<<":";
return o<<setw(2)<<t.second<<"\n"<<setfill(' ');
}
int main()
{
Time t(11, 59, 58);
cout<<t++;
cout<<++t;
}