在C语言里可用一个结构体来描述一种类型,如描述学生信息:
typedef struct {
char name[20];
int age;
int id;
}student_t;
如有三个学生,则:
student_t a, b, c;
通常情况下,用变量成员来描述类型的属性,但是类型的行为特征应用函数来描述.在结构体只能加入函数指针变量成员来描述.
typedef struct {
char name[20];
int age;
int id;
void (*study)(intwhat);
void (*eat)(intwhat);
}student_t;
结构使用起来时比较麻烦,创建一个对象都需要初始化对象的函数指针变量成员.
而且,在结构体里每个成员没有分权限来限制访问的,无法保证一些数据的完整性.
C++针对c的结构体引入类:
类里面可以直接实现函数成员。
类里面的成员可分权限:private protected public
1
2 #include<iostream>
3
4 using namespacestd;
5
6 class Student { // Student声明后,直接使用Student就是表示类型
7 public: //声明以下成员都是public的权限
8 string name; //注意:这里仅仅是定义这个类型有哪些成员,并没有分配具体的空间,所以这里的成员不能赋初始值
9 int id;
10 int age;
11
12 voidstudy(int what) {
13 cout <<"study " << what << endl;
14 }
15
16 void eat(intwhat) {
17 cout <<"eat " << what << endl;
18 }
19 };
20
21 int main(void)
22 {
23 Student a,b; //创建对象a和b,每个对象都有自己的属性成员,但函数成员是共用的(通过查看对象的大小可知).函数成员里不能写死针对某个对象的操作,只写要使用的成员名即可。
24
25 a.eat(888);
26 return 0;
27 }
public权限的成员,只要通过类的对象或者指向对象的指针变量都可以访问.
private权限的成员,只能在类的内部或者友员访问.如需改变类对象里的private权限成员的值时,应通过public的函数成员来改变. private成员就是不希望被直接访问的,访问也只能通过public函数成员来访问。
所以我们可以把一些数据隐藏起来,只在内部处理,别人无需访问,这就是封装。
1
2 #include<iostream>
3
4 using namespacestd;
5
6 class Student {
7 private:
8 string name;
9 int id;
10 int age;
11
12 public:
13 voidset_name(string n) {
14 name =n;
15 }
16
17 voidstudy(int what) {
18 cout <<name << " study " << what << endl;
19 }
20
21 };
22
23 int main(void)
24 {
25 Student a;
26
27 a.set_name("hehe");
28 a.study(23);
29
30
31 return 0;
32 }
私有成员也可以通过设置友员关系来访问,但这种方式破坏了封装性,尽量不要用.
1
2 #include<iostream>
3
4 using namespacestd;
5
6 class Student {
7 private:
8 string name;
9 int id;
10 int age;
11
12 public:
13 voidstudy(int what) {
14 cout <<name << " study " << what << endl;
15 }
16 friend intmain(void); //声明main函数是Student类型的朋友
17 // friend classxx;
18 };
19
20 int main(void)
21 {
22 Student a;
23
24 a.name ="lilei";
25 a.study(23);
26
27
28 return 0;
29 }
protected权限的成员,具有private的权限外,还可以让子类对象访问(后面再具体实现).
c++类里还可以有构造函数,析构函数。
构造函数在创建对象时自动被调用的,可用于初始对象里的属性成员的值
析构函数在回收对象时自动被调用的, 可用于对象回收前所需作的事情.
通常情况下构造函数成员是public的权限,某些场合可以是private的权限
析构函数都是public的权限
构造函数名与类名完全一致,没有返回值,连void返回类型也不能写。构造函数可重载.
析构函数名与类名一样(前面多一个”~”符号),没有返回值,析构函数不可重载.
1
2 #include<iostream>
3
4 using namespacestd;
5
6 class Student {
7 public:
8 Student() {
9 cout <<"student init" << endl;
10 }
11 ~Student() {
12 cout <<"student exit" << endl;
13 }
14 };
15
16 int main(void)
17 {
18 Student a;//如果是动态创建的对象需调用delete回收才会触发析构函数
19
20 cout <<"after object created" << endl;
21
22 return 0;
23 }
编译执行后输出:
[root@localhost06class]# ./a.out
student init
after object created
student exit
面试题,现有代码如下:
int main(void)
{
cout <<“hello” << endl;
return 0;
}
要求不能改动main函数里的代码,实现在”hello”输出前先输出”nono”.
实现方法:创建一个全局类对象,触发类的构造函数,构造函数里输出”nono”.
当自定义一个类型时,如没有实现构造函数,编译器会自动分配一个空的构造函数。
如果有实现构造函数,编译器就不会再分配空的构造函数了.
1
2 #include<iostream>
3
4 using namespacestd;
5
6 class Student {
7 private:
8 string name;
9
10 public:
11 Student(string n) {
12 name =n;
13 }
14
15 voidstudy(string what) {
16 cout <<name << " study " << what << endl;
17 }
18 };
19
20 int main(void)
21 {
22 Student a; //这里创建对象,需调用Student::Student()构造函数.通过错误信息也可以得知一个类也是一个名称空间.也可以得知,创建对象生成的代码里会有类构造函数的调用.
//c++里调用哪个函数,除了函数名以外,与函数参数个数,类型有关.
23
24
25 return 0;
26 }
编译输出:
[root@localhost06class]# g++ 04contructor.cpp
04contructor.cpp: Infunction ‘int main()’:
04contructor.cpp:22:10:error: no matching function for call to ‘Student::Student()’
Student a;
^
04contructor.cpp:22:10:note: candidates are:
04contructor.cpp:11:2:note: Student::Student(std::string)
Student(string n){
^
04contructor.cpp:11:2:note: candidate expects 1 argument, 0 provided
04contructor.cpp:6:7:note: Student::Student(const Student&)
class Student {
^
04contructor.cpp:6:7:note: candidate expects 1 argument, 0 provided
可修改为:
1
2 #include<iostream>
3
4 using namespacestd;
5
6 class Student {
7 private:
8 string name;
9
10 public:
11 Student(string n);
12 voidstudy(string what);
13 };
14
15 int main(void)
16 {
17 Studenta("superman"); // Student::Student(string)
18
19 a.study("Chinese");
20 return 0;
21 }
22
23Student::Student(string n)
24 {
25 name = n;
26 }
27
28 voidStudent::study(string what)
29 {
30 cout <<name << " study " << what << endl;
31 }
32
把双向循环链表封装成队列的例子.
1
2 #include<iostream>
3
4 using namespacestd;
5
6//定义节点类型,队列里装载多个节点对象
7 class Node {
8 public:
9 void *data;//装数据的地址
10 Node *prev,*next;
11
12 Node(Node*n1 = NULL, Node *n2 = NULL) {
13 //创建对象时,两个指针初始化为传进来的值,如不传则设NULL
14 prev =n1;
15 next =n2;
16 }
17 };
18
19 //定义对列类型
20 class MyQueue {
21 private:
22 Node *head;//链表头节点,只在内部使用
23
24 public:
25 MyQueue();
26 ~MyQueue();
27
28 intenqueue(void *data); //队列的入队函数
29 void*dequeue(); //队列的出队函数
30 };
31
32 int main(void)
33 {
34 MyQueue a,b; //每个对象就是一个队列,不同的队列可装载不同类型的数据.重用起来就很方便了.
35 int *p;
36
37 a.enqueue(new int(22));
38 a.enqueue(new int(33));
39 a.enqueue(new int(44));
40 a.enqueue(new int(55));
41
42
43 while (p =(int *)a.dequeue())
44 cout <<*p << endl;
45
46
47
48 return 0;
49 }
50
51 void*MyQueue::dequeue()
52 {
53 Node *n;
54 void *data;
55
56 //把链表头节点移除出队列,交返回数据的地址
57 if(head->next == head)
58 returnNULL; //链表里没有节点
59
60 n =head->next;
61
62 head->next= n->next;
63 n->next->prev = head;
64 data =n->data;
65 delete n;
66 return data;
67 }
68
69 intMyQueue::enqueue(void *data)
70 {
71 Node *n;
72 //创建一个链表的节点对象,初始化后加入链表的尾部
73
74 n = newNode;
75 if (NULL ==n)
76 return-1;
77 n->data =data;
78 n->next =head;
79 n->prev =head->prev;
80
81 head->prev->next = n;
82 head->prev= n;
83 return 0;
84 }
85
86
87MyQueue::MyQueue()
88 {
89 //初始化链表头节点,让头节点的prev,next指针指向自己本身
90 head = newNode;
91
92 head->prev= head;
93 head->next= head;
94 }
95
96
97MyQueue::~MyQueue()
98 {
99 Node *tmp,*tmp2;
100 //队列回收前,把所有链表的节点空间回收
101 for (tmp =head->next; tmp != head;)
102 {
103 tmp->prev->next = tmp->next;
104 tmp->next->prev = tmp->prev;
105 tmp2 =tmp->next;
106 delete(char *)tmp->data;
107 deletetmp;
108 tmp =tmp2;
109 }
110
111 delete head;
112 }
总结下C++的class比C的struct方便的特点:
1.class的成员可分成不同权限,可控制哪些成员是被别人访问的,哪些成员仅仅是内部使用.
struct的成员没有分权限的,只可以访问.
2.class可以直接写函数成员,这样更加直观地描述一种类型包括它的行为.
struct不可以直接写函数成员,只可以写函数指针成员,而且在使用函数指针成员前必须初始化指向一个有效的函数.
3.class里的函数成员的参数在使用类内部的属性成员时,不需要传递。在函数成员里可以直接访问类内部的属性成员.
4.class还有构造函数与析构函数,初始化工作与回收前的工作都会自动触发.