类是类型而不是数据对象,而每个类的对象都是该类数据成员的拷贝。然而,需要让类的所有对象在类的范围内共享某个数据。声明为static的类成员,便能在类范围中共享,称为静态成员。友元函数完全是普通的C++函数,不同的是,它可以访问类的保护或私有成员,方便编程,提高了效率,但却破坏了类的封装。
1、静态成员的需要性:
有一些属性是类中所有对象所共有的。
2、静态成员的使用:
成员由数据成员和成员函数之分,静态成员,也有静态数据成员,静态成员函数之分。静态成员用static声明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
# include <iostream> # include <string> using namespace std; class student { public : student(char * name); ~ student(); static int number() { return num; } private : static int num; //默认初始化为0,如果我们给他赋值为0,则这样是非法的。 char pname[ 40 ]; }; int student::num = 0 ; //静态数据成员在类声明外分配空间和初始化 student:: student(char * name) { cout << "创建了一个学生对象:" << name <<endl; if (name != 0 ) { strcpy(pname, name); } num += 1 ; cout << "学生人数是:" << num << endl; } student::~ student() { cout << "销毁学生对象:" << pname << endl; num -= 1 ; //每析构一次学生人数减少一个 cout << "学生人数:" << num << endl; } void fn() { student s1( "ws" ); student s2( "wc" ); cout << student::number() << endl; } void main() { fn(); cout << student::number() << endl; system( "pause" ); return ; } |
数据成员num既不是对象s1也不是对象s2的一部分。student类随着对象的产生,每个对象都有一个name成员值,但无论对象有多少,甚至没有,静态成员num也只有一个。所有student类的对象都能共享他,访问他。
静态数据成员是类的一部分,静态数据成员的定义是类定义的一部分,将其放在类的内部实现部分中定义在合适不过了。定义时要用类名引导。重用该类时,简单的包含其头部文件即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# include <iostream> # include <string> using namespace std; #ifndef STUDENT_H #define STUDENT_H class Student { public : Student(char *); ~ Student(); static int number() { return numbers; } private : static int numbers; char pname[ 30 ]; }; int Student::numbers = 0 ; //静态数据成员在类外声明和初始化 Student:: Student(char * name) { if (name != 0 ) { strcpy(pname, name); } cout << "创建了一个学生对象:" << pname << endl; numbers += 1 ; } Student::~ Student() { cout << "销毁学生对象:" << pname << endl; numbers -= 1 ; //每析构一次学生人数减少一个 cout << "学生人数:" << numbers << endl; } #endif |
3、静态数据成员:
公共静态数据成员可被类的外部访问,保护或私有静态数据成员只可被类的内部访问。
类名::静态成员名(意义是静态数据成员是属于student类的,而不是属于哪个特定对象的,他也不需要依赖某个特定对象的数据。)
例如:下面的代码返回对象引用的成员函数作为对象值去操作静态成员,但是静态成员只取返回对象的类型,其返回函数未被执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# include <iostream> # include <string> using namespace std; # include "student.h" void fn1() { Student s1( "ws" ); Student s2( "wc" ); cout << Student::number() << endl; } void fn2(Student& s) { cout << s.nextstudent().numbers2 << endl; } void main() { /* fn1(); cout << Student::number() << endl; system("pause"); return; */ Student ss( "ws" ); fn2(ss); system( "pause" ); return ; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
# include <iostream> # include <string> using namespace std; #ifndef STUDENT_H #define STUDENT_H class Student { public : Student(char *); static int number() //静态成员函数 { return numbers; } static int numbers2; //公共静态数据成员 Student & nextstudent() { numbers2 += 1 ; return * this ; } ~Student(); private : static int numbers; char pname[ 30 ]; }; int Student::numbers2 = 0 ; int Student::numbers = 0 ; //静态数据成员在类外声明和初始化 Student:: Student(char * name) { if (name != 0 ) { strcpy(pname, name); } cout << "创建了一个学生对象:" << pname << endl; numbers += 1 ; } Student::~ Student() { cout << "销毁学生对象:" << pname << endl; numbers -= 1 ; //每析构一次学生人数减少一个 cout << "学生人数:" << numbers << endl; } #endif |
静态数据成员用的比较多的场合是:
(1)用来保存流动变化的对象个数
(2)作为一个标志,指示 一个特定的动作是否发生
(3)一个指向链表第一成员或最后一个成员的指针。
例如:下面的程序描述一个学生类,该类对象是一个个的学生,他们构成一个单向链表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
# include <iostream> # include <string> using namespace std; #ifndef STUDENT_H #define STUDENT_H class Student { public : Student(char *); static int number() //静态成员函数 { return numbers; } static int numbers2; //公共静态数据成员 Student & nextstudent() { numbers2 += 1 ; return * this ; } ~Student(); private : static int numbers; char pname[ 30 ]; static Student *pfirst; Student *pnext; }; Student * Student::pfirst = 0 ; int Student::numbers2 = 0 ; int Student::numbers = 0 ; //静态数据成员在类外声明和初始化 Student:: Student(char * name) { if (name != 0 ) { strcpy(pname, name); } cout << "创建了一个学生对象:" << pname << endl; numbers += 1 ; pnext = pfirst; pfirst = this ; } Student::~ Student() { cout << "销毁学生对象:" << pname << endl; numbers -= 1 ; //每析构一次学生人数减少一个 cout << "学生人数:" << numbers << endl; if (pfirst == this ) //如果要删除链首节点,则只要链首指针指向下一个 { pfirst = pnext; return ; } for (Student *ps = pfirst; ps; ps = ps->pnext) { if (ps->pnext = this ) //找到时,ps指向当前节点的节点 { ps->pnext = this ->pnext; return this ; } } } #endif |
实现链表结构的学生类,需要一个链首指针,pfirst,每个对象都需要指向下一个对象的指针pnext,所以pnext数据成员不是静态的,而pfirst数据成员是静态的。
链表操作是在构造函数和析构函数中进行的。构造中增加节点的处理比从析构中删除一个节点要相对容易一些。删除一个学生节点时,先要在链表中找到当前节点(this指向的节点),然后把前一个节点和后一个节点连接起来。在实现中,找到当前节点位置时,保存指向当前节点的指针是至关重要的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
# include <iostream> # include <string> using namespace std; # include "student.h" void fn1() { Student s1( "ws" ); Student s2( "wc" ); cout << Student::number() << endl; } void fn2(Student& s) { cout << s.nextstudent().numbers2 << endl; } Student * fn3() { Student *ps = new Student( "wangshuai" ); //不仅创建一个wangshuai对象而且, //定义了一个指针ps指向这块堆内存 return ps; } void main() { /* fn1(); cout << Student::number() << endl; system("pause"); return; */ /* Student ss("ws"); fn2(ss); system("pause"); return; */ Student sa( "java" ); Student *sc=fn3(); Student *sb = fn3(); delete sb; //delete函数会自动调用析构函数 delete (sc); system( "pause" ); } |
注意:delete函数调用析构函数,delete的参数类型一定是指针,所以析构函数中一定要将链表中析构掉的这个节点从链表中删除,并将链表在连接起来。
4、静态成员函数:
静态成员函数定义是类的内部实现,属于类定义的一部分。他的定义位置与一般成员函数一样。
与静态数据成员一样,静态成员函数与类相联系,不与类的对象相联系,所以访问静态成员函数时,不需要对象。如果用对象去引用静态成员函数,只是用器类型。
一个静态成员函数不与任何对象相联系,故不能对非静态成员进行默认访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
# include <iostream> # include <string> using namespace std; #ifndef STUDENT_H #define STUDENT_H class Student { public : Student(char *); static int number() //静态成员函数 { return numbers; } static Student * findname(char * name); static int numbers2; //公共静态数据成员 Student & nextstudent() { numbers2 += 1 ; return * this ; } ~Student(); private : static int numbers; char pname[ 30 ]; static Student *pfirst; static Student *pnext; }; Student * Student::pfirst = 0 ; Student * Student::pnext = 0 ; int Student::numbers2 = 0 ; int Student::numbers = 0 ; //静态数据成员在类外声明和初始化 Student:: Student(char * name) { if (name != 0 ) { strcpy(pname, name); } cout << "创建了一个学生对象:" << pname << endl; numbers += 1 ; if (pfirst == 0 ) { pfirst = this ; pnext = this ; } else { (*pnext).pnext = this ; pnext = pnext->pnext; } } Student *Student::findname(char * name) { for (Student * ps = pfirst; ps; ps = ps->pnext) { if (strcmp(ps->pname, name) == 0 ) { return ps; } cout << "111" << endl; return (Student*) 0 ; } } Student::~ Student() { cout << "销毁学生对象:" << pname << endl; numbers -= 1 ; //每析构一次学生人数减少一个 cout << "学生人数:" << numbers << endl; if (pfirst == this ) //如果要删除链首节点,则只要链首指针指向下一个 { pfirst = pnext; return ; } for (Student *ps = pfirst; ps; ps = ps->pnext) { if (ps->pnext = this ) { ps->pnext = this ->pnext; //把前一个节点和后一个节点连接起来 return ; } } } #endif |
静态成员函数与非静态成员函数的根本区别是什么?他们的根本区别就是静态成员函数没有this指针,而非静态成员函数有一个指向当前对象的指针this。
5、需要友元的原因:
有时,普通函数需要直接访问一个类的保护或私有数据成员。如果没有友元机制,则只能将类的数据成员声明为公共的,从而,任何函数都可以无拘无束的访问它。
普通函数有时需要直接访问类的保护私有数据成员的主要原因是为了提高效率。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
# include <iostream> # include <stdlib.h> using namespace std; class Vector { public : Vector( int ); Vector(Vector &); int size() { return sz; } void display() { for ( int i = 0 ; i < sz; i += 1 ) { cout << v[i] << " " ; cout << endl; } } int & elem( int i) //引用返回的目的是返回值可以做左值 { if (i < 0 || sz <= i) { cerr << "vector index out of range:" << "
" ; exit(- 1 ); } } ~Vector(); private : int *v; //指向一个数组,表示向量 int sz; //元素个数 }; Vector::Vector(Vector &vec) { v = new int [sz = vec.sz]; memcpy(( void *)v, ( void *)vec.v, sz*sizeof( int )); } Vector::Vector( int s) { if (s <= 0 ) { cerr << "bad Vector size" << endl; exit(- 1 ); } sz = s; v = new int [s]; //从堆中分配一个数组存放向量元素 } Vector::~Vector() { delete []v; //将堆中数组空间返还 } class Matrix { public : Matrix( int , int ); Matrix(Matrix &); int sizel() { return sz1; } int sizer() { return szr; } int & elem( int , int ); ~Matrix(); private : int * m; int sz1; int szr; }; Matrix::Matrix( int i, int j) { if (i <= 0 || j <= 0 ) { cerr << "bad matrix size" << "
" ; exit(- 1 ); } sz1 = i; szr = j; m = new int [i*j]; } Matrix::Matrix(Matrix & mat) { sz1 = mat.sz1; szr = mat.szr; m = new int [sz1*szr]; memcpy(( void *)m, ( void *)mat.m,(szr* sizeof( int ))); } int & Matrix::elem( int i, int j) //引用返回的目的是返回值可以做左值 { if (i < 0 || sz1 <= i || j < 0 || szr <= j) { cerr << "matrix index out of range:" ; exit(- 1 ); } return m[i*szr + j]; } Matrix::~Matrix() { delete []m; } Vector Multiply(Matrix&m, Vector&v) //矩阵乘向量的普通函数 { if (m.sizer() != v.size()) { cerr << "bad multiply matrix with vector:" ; exit( 1 ); } Vector r(m.sizel()); //创建一个存放结果的空向量 for ( int i = 0 ; i < m.sizel; i += 1 ) { r.elem(i) = 0 ; for ( int j = 0 ; j < m.sizer(); j += 1 ) { r.elem(i) += m.elem(i, j)*v.elem(j); } return r; } } void main() { Matrix ma( 4 , 3 ); ma.elem( 0 , 0 ) = 1 ; ma.elem( 0 , 1 ) = 2 ; ma.elem( 0 , 2 ) = 3 ; ma.elem( 1 , 0 ) = 0 ; ma.elem( 1 , 1 ) = 1 ; ma.elem( 1 , 2 ) = 2 ; ma.elem( 2 , 0 ) = 1 ; ma.elem( 2 , 1 ) = 1 ; ma.elem( 2 , 2 ) = 3 ; ma.elem( 3 , 0 ) = 1 ; ma.elem( 3 , 1 ) = 2 ; ma.elem( 3 , 2 ) = 1 ; Vector ve( 3 ); ve.elem( 1 ) = 1 ; ve.elem( 2 ) = 0 ; Vector va = Multiply(ma, ve); va.display(); system( "pause" ); return ; } |
在类里声明一个普通函数,标上关键字friend,就成了该类的有缘(友元),可以访问该类中的一切成员。
使用友元避免了频繁调用成员函数,效率就大大提高了。
需要友元的另外一个原因就是为了方便重载操作符的使用。
友元函数不是成员函数,它是类的朋友,因而能够访问类的全部成员。在类的内部,只能声明他的函数原型,加上friend关键字。有缘声明可以在类内部的任何位置,意义完全一样。友元函数的定义则在类的外部,一般与类的成员函数定义放在一起。因为类重用时,一般友元是一起提供的。
一个类的成员函数可以是另一个类的友元。
整个类可以是另一个类的友元,该友元称为友类。友类的每个成员函数都可访问另一个类中的保护数据成员。
小结:
使用静态数据成员可以消灭全局变量。全局变量给面向对象程序带来的问题就是违背封装原则。使用静态数据成员就必须在main()程序运行之前分配内存空间和初始化。使用静态成员函数,可以在实际创建任何对象之前初始化专有的静态数据成员。静态成员不与类的任何特定对象相关联。
静态的static一词,与静态存储类的static是两个概念,一个论及类,一个论及内存空间的位置以及作用域限定。所以要区分静态对象和静态成员。
友元的作用主要是为了提高效率和方便编程。