19129
备注:
|
20794
|
删除的内容标记成这样。 | 加入的内容标记成这样。 |
行号 15: | 行号 15: |
int main() { complex x, y; x.real = 10.0; x.imag = 20.0; y.real = 1.0; y.imag = -2.0; complex z = add(x, y); } |
|
行号 32: | 行号 41: |
}}} 再一个例子: {{{#!cplusplus struct Student{ char num[20]; char name[10]; bool gender; }; void display(Student &s) { cout << s.num << s.name << (s.gender?"male":"female"); } int main() { Student stud1, stud2; // initialize display(stud1); display(stud2); } |
|
行号 54: | 行号 83: |
int main() { Clock c1, c2; c1.SetTime(10, 10, 30); c2.SetTime(18, 00, 00); c1.ShowTime(); c2.ShowTime(); } |
|
行号 73: | 行号 110: |
}}} | // main.cpp源文件 int main() { Clock c1, c2; c1.SetTime(10, 10, 30); c2.SetTime(18, 00, 00); c1.ShowTime(); c2.ShowTime(); } }}} 另一个例子: {{{#!cplusplus struct Student{ char num[20]; char name[10]; bool gender; void diplay() { cout << num << name << (gender?"male":"female"); } }; int main() { Student stud1, stud2; // initialize stud1.display(); stud2.display(); } }}} |
行号 167: | 行号 232: |
int hour, minute, second; | int hour, minute, second; |
行号 169: | 行号 234: |
void SetTime(int yy, int mm, int dd); void ShowTime(); |
void SetTime(int yy, int mm, int dd); void ShowTime(); |
行号 196: | 行号 261: |
}}} 另一个例子: {{{#!cplusplus class Student{ private: char num[20]; char name[10]; bool gender; public: void display() { cout << num << name << (gender?"male":"female"); } void setnum(char n[]) { strcpy(num, n); } void setname(char n[]) { strcpy(name, n); } void setgender(bool g) { gender = g; } }; int main() { Student stud1; stud1.setname("jack"); stud1.setnum("05020001"); stud1.setgender(true); stud1.display(); cout << stud1.gender; //error } |
C++类与对象
1. 问题
C++语言只提供了整数、浮点数、bool、字符等基本类型。如何处理系统没有内置的类型?比如复数?时间?日期?坐标?传统C语言的做法是使用结构体,比如:
1 struct complex {
2 double real;
3 double imag;
4 };
5 complex add(complex a, complex b);
6 complex substract(complex a, complex b);
7 complex multiply(complex a, complex b);
8
9 int main() {
10 complex x, y;
11 x.real = 10.0;
12 x.imag = 20.0;
13 y.real = 1.0;
14 y.imag = -2.0;
15 complex z = add(x, y);
16 }
另一个例子:
再一个例子:
2. C++的类
C++的做法是在C语言的基础上更进一步,将数据与操作这组数据的函数结合在一起,构成类(class)
C++中类的定义可以用struct或者用class。函数与数据结合在一起,逻辑关系更加明确。类中的函数又被称为方法、成员函数。C++类中函数的定义,函数体可以直接写在类的内部,写在头文件中:
1 struct Clock {
2 int hour, minute, second;
3 void SetTime(int h, int m, int s) {
4 hour = h; minute = m; second = s;
5 }
6 void ShowTime() {
7 cout << hour << minute << second;
8 }
9 };
10
11 int main() {
12 Clock c1, c2;
13 c1.SetTime(10, 10, 30);
14 c2.SetTime(18, 00, 00);
15 c1.ShowTime();
16 c2.ShowTime();
17 }
也可以分开定义,比如:
1 //clock.h头文件:
2 struct Clock {
3 int hour, minute, second;
4 void SetTime(int h, int m, int s);
5 void ShowTime();
6 };
7
8 //clock.cpp源文件:
9 void Clock::SetTime(int h, int m, int s) {
10 hour = h;
11 minute = m;
12 second = s;
13 }
14 void Clock::ShowTime() {
15 cout << hour << minute << second;
16 }
17
18 // main.cpp源文件
19 int main() {
20 Clock c1, c2;
21 c1.SetTime(10, 10, 30);
22 c2.SetTime(18, 00, 00);
23 c1.ShowTime();
24 c2.ShowTime();
25 }
另一个例子:
对象的定义和访问方式类似于结构体
在堆空间分配和访问对象
定义对象数组
成员函数和普通函数一样可以内联。函数体写在类定义内部的成员函数,默认就是内联的。
类外定义函数体的成员函数,要定义成内联需要加inline关键字,并把函数体写在头文件中
成员函数也可以重载
成员函数也可以有缺省参数
3. 类的访问权限
公有部分定义了类的外部接口,可供类的使用者调用。私有部分隐藏了类的具体实现,由类的实现者实现,不需要使用者关心。这就是封装。private和public为新增的关键字。
struct与class的访问权限的区别:struct默认public,class默认为private
另一个例子:
1 class Student{
2 private:
3 char num[20];
4 char name[10];
5 bool gender;
6 public:
7 void display() {
8 cout << num << name << (gender?"male":"female");
9 }
10 void setnum(char n[]) {
11 strcpy(num, n);
12 }
13 void setname(char n[]) {
14 strcpy(name, n);
15 }
16 void setgender(bool g) {
17 gender = g;
18 }
19 };
20
21 int main() {
22 Student stud1;
23 stud1.setname("jack");
24 stud1.setnum("05020001");
25 stud1.setgender(true);
26 stud1.display();
27 cout << stud1.gender; //error
28 }
4. 构造与析构
定义变量的同时完成初始化(resource acquisition is initialization)是一种好的编程习惯,可以避免错误。
为避免偶然使用没有初始化的对象的错误,可以在类中定义一个特殊的函数——构造函数。它在对象被定义时,就被自动调用,以确保完成初始化。
构造函数的名字与类名相同,没有返回类型。它利用特定的值构造对象,把对象初始化为一个特定的状态。
带构造函数的对象定义
构造函数可以重载,也可以有缺省参数:
各个构造函数所需要参数不同,但构造函数都没有返回类型。在定义对象时,会根据传递的参数来选择一个特定的构造函数初始化对象。
一个类要能够默认构造对象,需要:这个类一个构造函数都没有写(系统会自动生成一个默认构造函数),或者至少写有一个默认构造函数。一个所有参数具有默认参数的构造函数也是默认构造函数,比如
对象被摧毁时,一个成员函数也会自动被调用,这个函数称为称为析构函数,一般完成资源的释放工作
析构函数的名字为类名前加~,没有返回类型和参数。析构函数不能重载。例如:
new会自动调用构造函数,而malloc不能。delete会自动调用析构函数,而free不能。
要定义对象数组,并且没有初始化,那么要求该类可以默认构造对象。比如:
5. 对象的拷贝构造
对象在两种情况下发生复制:赋值和拷贝构造。拷贝构造是指创建一个对象,创建的同时让它的值与另一个已存在的对象一模一样。赋值是复制另一个同类型的值给已存在的对象,使它变成新的值。比如
拷贝构造的工作由拷贝构造函数完成。如果自己没有编写拷贝构造函数,编译器会自动生成一个拷贝构造函数,用于完成缺省的拷贝构造的功能:用源对象的所有数据成员逐一拷贝构造目标对象的相应成员。我们可以自定义拷贝构造函数去改写默认的拷贝构造函数。
何时需要自定义拷贝构造函数、析构函数?当涉及到内存管理时,一个类中有指针成员,指向动态分配的空间,那么通常需要写一组拷贝构造函数、析构函数和赋值操作符。写一个类Array,模拟数组的功能,元素是int类型,数组的大小可以是变量,增加检查越界的功能。即可以这样使用:
但是这个类在这样使用时存在问题:
拷贝构造的对象与原对象共享同一块空间,修改了一个对象,另一个对象也受影响。当一个对象释放时,另一个对象也不能使用了。当两个对象都被释放时,同一个空间释放了两次。解决办法是自定义拷贝构造函数:
1 class Array {
2 private:
3 int *p;
4 int size;
5 public:
6 Array(int s) {
7 size = s;
8 p = new int [ size ];
9 }
10 ~Array() {
11 delete[ ] p;
12 }
13 int & at( int i) {
14 if( i < size)
15 return p[i];
16 else
17 return 0;
18 }
19 Array( Array &a) {
20 size = a.size;
21 p = new int [size];
22 for(int i = 0; i < size; i++)
23 p[i] = a.p[i];
24 }
25 };
6. 类的组合
构造复杂的对象的一种方法是组合。一个类可以使用另一个类的对象作为成员,比如:
1 class point{
2 private:
3 double x, y;
4 public:
5 //...
6 };
7
8 class line {
9 private:
10 point start, end;
11 public:
12 //...
13 };
14
15 class circle {
16 private:
17 point center;
18 double radius;
19 public:
20 //...
21 };
22
23 class rectangle {
24 private:
25 point p1, p2;
26 public:
27 //...
28 };
组合对象的构造
1 class point{
2 public:
3 double x, y;
4 point(double x0 = 0, double y0 = 0) {
5 x = x0;
6 y = y0;
7 cout << "point " << x << y << "initialized!" << endl;
8 }
9 };
10
11 class line {
12 public:
13 point start, end;
14 line(double x0, double y0, double x1, double y1) {
15 cout << "line initializing" << endl;
16 start.x = x0;
17 start.y = y0;
18 end.x = x1;
19 end.y = y1;
20 }
21 };
从这个程序的运行结果可以看出,在line类的构造函数执行前,start和end对象已经构造完成。point类没有默认构造函数,line不能构造。如果x、y是私有的,那么在line构造函数中不能对他们进行赋值。解决办法:使用构造函数初始化列表
1 class point{
2 private:
3 double x, y;
4 public:
5 point(double x0, double y0) {
6 x = x0;
7 y = y0;
8 cout << "point " << x << y << "initialized!" << endl;
9 }
10 };
11
12 class line {
13 private:
14 point start, end;
15 public:
16 line(double x0, double y0, double x1, double y1)
17 : start(x0, y0), end(x1, y1) {
18 cout << "line initializing" << endl;
19 }
20 };
在line构造函数的初始化列表中,给start、end成员的构造函数传递参数完成它们的初始化。
初始化表还能够解决其它一些类型的成员的初始化问题,比如const成员,引用成员等。比如
构造函数和析构函数执行的顺序
1 class point{
2 private:
3 double x, y;
4 public:
5 point(double a, double b) :x(a), y(b) {
6 cout << "construct point" << endl;
7 }
8 ~point() {
9 cout << "destruct point" << endl;
10 }
11 };
12 class line {
13 private:
14 point start, end;
15 public:
16 line(double x0, double y0, double x1, double y1)
17 : start(x0, y0) , end(x1, y1) {
18 cout << "construct line" << endl;
19 }
20 ~line() {
21 cout << "destruct line" << endl;
22 }
23 };
7. 静态成员
C++的类有一种特殊的成员,它的空间是这个类的所有对象共享的,称为类静态数据成员
静态成员变量的定义、初始化和访问
1 class Task {
2 public:
3 Task() : x(n) { n++; }
4 ~Task() { n--; }
5 int x;
6 static unsigned n;
7 };
8 unsigned Task::n = 0;
9
10 int main() {
11 Task s1;
12 cout << s1.x << s1.n << endl;
13 Task s2;
14 cout << s1.x << s1.n << endl;
15 cout << s2.x << s2.n << endl;
16 cout << Task::n << endl;
17 }
静态成员函数:只能访问静态成员的成员函数
静态成员用法举例:写一个类Singleton,这个类只能存在一个对象。
8. 友元
友元提供了让其它函数或类直接访问私有成员的途径。友元函数是能够直接访问类的私有成员的函数。
class Point { private: int x, y; public: int GetX() { return x; } friend bool isequal(Point a, Point b); }; bool isequal(Point a, Point b) { return a.x == b.x && a.y == b.y; }
友元类
友元关系是单向的,并不可传递。
== const对象 ==
定义对象前面加const,表示对象不能改变
1 class Clock{
2 int hour, minute, second;
3 public:
4 Clock(int h, int m, int s)
5 : hour(h), minute(m), second(s)
6 {
7 }
8 void SetTime(int h, int m, int s) {
9 hour = h;
10 minute = m;
11 second = s;
12 }
13 void ShowTime() {
14 cout << hour << minute << second;
15 }
16 };
17 int main() {
18 const int i = 10;
19 i++; //error!
20 const Clock t(10, 30, 20);
21 cout << t.second;
22 t.second = 10; // error
23 t.SetTime(10, 20, 10); // error!
24 t.ShowTime(); // error!
25 }
成员函数声明后面加const,表示这个成员函数不修改数据成员。这样的函数可以被const对象调用。
常成员函数可以被常对象调用,也可以被普通对象调用。
常成员函数与非常成员函数可以重载
1 class Array {
2 private:
3 int *p, size;
4 public:
5 Array(int s) {
6 size = s;
7 p = new int [ size ];
8 }
9 ~Array() { delete[ ] p; }
10 int & at( int i) {
11 if( i < size) return p[i]; else throw out_of_range();
12 }
13 int at( int i) const {
14 if( i < size) return p[i]; else throw out_of_range();
15 }
16 };
17 int main() {
18 Array a(10);
19 a.at(5) = 5;
20 const Array b(5);
21 b.at(5) = 5; // error
22 }
9. this指针
1 void f() {
2 Clock time(1999, 9, 9);
3 time.set_second(20);
4 time.set_minute(30)
5 time.set_hour(10);
6 time.set_second(20).set_minute(30).set_hour(10);
7 }
8 class Clock {
9 public:
10 Clock &set_second(int n);
11 Clock &set_minute(int n);
12 Clock &set_hour(int n);
13 };
14 Clock &Clock::set_second(int n) {
15 second = n;
16 return *this;
17 }
this有两种类型:以Clock类为例,Clock* const和const Clock* const