14263
备注:
|
29433
|
删除的内容标记成这样。 | 加入的内容标记成这样。 |
行号 15: | 行号 15: |
void display(complex a); 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); display(z); } |
|
行号 19: | 行号 30: |
struct Clock { | struct clock { |
行号 24: | 行号 35: |
void SetTime( Clock *c, int h, int m, int s) { | void set_time( clock *c, int h, int m, int s) { |
行号 29: | 行号 40: |
void ShowTime( Clock *c) { | void show_time(const clock *c) { |
行号 32: | 行号 43: |
int main() { clock c; set_time(&c, 10, 10, 30); show_time(&c); } }}} 再一个例子: {{{#!cplusplus #define MALE true #define FEMALE false struct student{ char num[20]; char name[10]; bool gender; }; void set(student &s, char id[], char n[], bool g){ strcpy(s.num, id); strcpy(s.name, n); s.gender = g; } void display(student &s) { cout << s.num << s.name << (s.gender?"male":"female"); } int main() { student stud1, stud2; // initialize set(stud1, "110101", "Rose", FEMALE); set(stud2, "110102", "JACK", MALE); display(stud1); display(stud2); } |
|
行号 35: | 行号 81: |
C++的做法是在C语言的基础上更进一步,将数据与操作这组数据的函数结合在一起,构成类(class) {{{#!cplusplus struct Clock { |
C语言中,表示数据的结构体和操作这些结构体的函数是分开的。或者说数据结构和操作它的算法是分开的。这样数据和操作之间的关系不是很清晰。C++的做法是在C语言的基础上更进一步,将数据与操作这组数据的函数结合在一起,构成类(class) {{{#!cplusplus struct clock { |
行号 39: | 行号 85: |
void SetTime(int h, int m, int s); void ShowTime(); }; }}} C++中类的定义可以用struct或者用class。函数与数据结合在一起,逻辑关系更加明确。类中的函数又被称为方法、成员函数。C++类中函数的定义,函数体可以直接写在类的内部,写在头文件中: {{{#!cplusplus struct Clock { |
void set_time(int h, int m, int s); void show_time(); }; }}} C++中类的定义可以用struct或者用class。函数与数据结合在一起,逻辑关系更加明确。定义在类中的函数又被称为方法、成员函数。成员函数可以直接访问类(结构体)中的数据成员。C++类中函数的定义,函数体可以直接写在类的内部,写在头文件中: {{{#!cplusplus struct clock { |
行号 47: | 行号 93: |
void SetTime(int h, int m, int s) { hour = h; minute = m; second = s; } void ShowTime() { |
void set_time(int h, int m, int s) { hour = h; minute = m; second = s; } void show_time() { |
行号 54: | 行号 102: |
int main() { clock c1, c2; c1.set_time(10, 10, 30); c2.set_time(18, 00, 00); c1.show_time(); c2.show_time(); } |
|
行号 58: | 行号 114: |
struct Clock { | struct clock { |
行号 60: | 行号 116: |
void SetTime(int h, int m, int s); void ShowTime(); |
void set_time(int h, int m, int s); void show_time(); |
行号 65: | 行号 121: |
void Clock::SetTime(int h, int m, int s) { | void clock::set_time(int h, int m, int s) { |
行号 70: | 行号 126: |
void Clock::ShowTime() { | void clock::show_time() { |
行号 73: | 行号 129: |
}}} | // main.cpp源文件 int main() { clock c1, c2; c1.set_time(10, 10, 30); c2.set_time(18, 00, 00); c1.show_time(); c2.show_time(); } }}} 另一个例子: {{{#!cplusplus const bool MALE = true; const bool FEMALE = false; struct student{ string num; string name; bool gender; void set(string id, string n, bool g) { num = id; name = n; gender = g; } void diplay() { cout << num << name << (gender?"male":"female"); } }; int main() { student stud1, stud2; stud1.set("110101", "Rose", FEMALE); stud2.set("110102", "Jack", MALE); stud1.display(); stud2.display(); } }}} |
行号 78: | 行号 170: |
struct Clock now; // 类似于C语言结构体 Clock next; // struct可以省略 next.hour = 0; // 同C语言结构体类似,可以访问成员 now.SetTime(9, 40, 0); // 可以用类似的方式调用成员函数 now.ShowTime(); next.SetTime(9, 40, 1); next.ShowTime(); |
struct clock now; // 类似于C语言结构体 clock next; // struct可以省略 next.hour = 10; // 同C语言结构体类似,可以访问成员 next.minute = 10; next.second = 30; now.set_time(9, 40, 0); // 可以用类似的方式调用成员函数 now.show_time(); next.show_time(); |
行号 91: | 行号 184: |
Clock * my_clock = new Clock; //分配对象 my_clock->hour = 10; //通过指针访问成员 my_clock->SetTime (9, 40, 0); //通过指针调用成员函数 my_clock->ShowTime(); |
clock * my_clock = new clock; //分配对象 clock * now = new clock; now->hour = 10; //通过指针访问成员 now->minute = 10; now->second = 30; my_clock->set_time (9, 40, 0); //通过指针调用成员函数 my_clock->show_time(); |
行号 96: | 行号 192: |
delete now; | |
行号 102: | 行号 199: |
Clock clocks[100]; // 类似于int array[100]; clocks[0].SetTime(9, 10, 25); clocks[1].SetTime(9, 9, 13); clocks[2].SetTime(9, 12, 25); |
clock clocks[100]; // 类似于int array[100]; clocks[0].set_time(9, 10, 25); clocks[1].set_time(9, 9, 13); clocks[2].set_time(9, 12, 25); |
行号 107: | 行号 204: |
Clock *pc = new Clock[100]; pc[0].SetTime(11, 20, 30); |
clock *pc = new clock[100]; pc[0].set_time(11, 20, 30); |
行号 116: | 行号 213: |
struct Clock { | struct clock { |
行号 118: | 行号 215: |
inline void ShowTime() { //inline may be omitted | inline void show_time() { //inline may be omitted |
行号 126: | 行号 223: |
inline void Clock::ShowTime() { | struct clock { int second, minute, hour; inline void show_time(); }; inline void Clock::show_time() { |
行号 133: | 行号 234: |
struct Clock { | struct clock { |
行号 135: | 行号 236: |
void SetTime(int h, int m) { | void set_time(int h, int m) { |
行号 140: | 行号 241: |
void SetTime(int h) { | void set_time(int h) { |
行号 146: | 行号 247: |
Clock t; t.SetTime( 10, 30); t.SetTime( 10 ); } }}} 成员函数也可以有缺省参数 {{{#!cplusplus struct Clock { |
clock t; t.set_time( 10, 30); t.set_time( 10 ); } }}} 成员函数也可以有缺省参数。缺省值写在声明中,而不是定义中。 {{{#!cplusplus struct clock { |
行号 156: | 行号 257: |
void SetTime(int h =0, int m=0, int s=0); }; void Clock::SetTime(int h, int m, int s) { hour = h; minute = m; second = s; |
void set_time(int h =0, int m=0, int s=0); }; void clock::set_time(int h, int m, int s) { hour = h; minute = m; second = s; |
行号 164: | 行号 267: |
{{{#!cplusplus class Clock { |
在前面的clock类中,我们发现类中的数据成员hour、minute、second不需要被clock类的使用者直接访问。如果直接访问,还可能会带来副作用。在C++中增加了对类的成员的访问权限的控制,把成员分为public和private等。 {{{#!cplusplus class clock { |
行号 167: | 行号 271: |
int hour, minute, second; | int hour, minute, second; |
行号 169: | 行号 273: |
void SetTime(int yy, int mm, int dd); void ShowTime(); }; }}} 公有部分定义了类的外部接口,可供类的使用者调用。私有部分隐藏了类的具体实现,由类的实现者实现,不需要使用者关心。这就是封装。private和public为新增的关键字。 |
void set_time(int yy, int mm, int dd); void show_time(); }; }}} public部分定义了类的外部接口,可供类的使用者调用。private部分隐藏了类的具体实现,由类的实现者实现,不需要使用者关心。这就是‘’‘封装’‘’。private和public为新增的关键字。 |
行号 176: | 行号 280: |
Clock d; d.SetTime(8, 27, 0); // 访问public, ok d.hour = 10; // 访问private错误 } void Clock::SetTime(int h, int m, int s) { hour = h; //访问private, ok |
clock d; d.set_time(8, 27, 0); // 类内访问public, ok d.hour = 10; // 类内访问private错误 } void clock::set_time(int h, int m, int s) { hour = h; //类内访问private, ok |
行号 189: | 行号 293: |
struct ClockA { | struct clock_a { |
行号 193: | 行号 297: |
class ClockB { | class clock_b { |
行号 198: | 行号 302: |
另一个例子: {{{#!cplusplus class student{ private: string num; string name; bool gender; public: void display() { cout << num << name << (gender?"male":"female"); } void setnum(string n) { num = n; } void setname(char n) { 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 } }}} '''并非'''所有数据成员都必须是private,'''并非'''所有成员函数都必须是公有的。 {{{#!cplusplus class clock { private: //只能在类内访问 int hour, minute, second; public: //可以在类外访问 void add(int s) { for(int i = 0; i < s; i++) inc(); } private: void inc() { second++; if(second == 60) { second = 0; minute ++; if(minute == 60) { minute = 0; hour++; if(hour == 24) hour = 0; } } } }; }}} |
|
行号 202: | 行号 365: |
int i = 5; // 定义普通变量,定义的同时初始化 cout << i; // 访问变量 Clock time; // 定义对象,定义了以后没有初始化 time.ShowTime(); //错误,没有初始化 time.SetTime(2000, 5, 1); //需要调用初始化函数后 time.ShowTime(); //才能够访问 |
int main() { int i = 5; // 定义普通变量,定义的同时初始化 cout << i; // 访问变量 clock time; // 定义对象,定义的同时并没有初始化 time.show_time(); //错误,没有初始化 time.set_time(2000, 5, 1); //需要调用初始化函数后 time.show_time(); //才能够访问 } |
行号 212: | 行号 377: |
class Clock{ public: Clock(int h, int m, int s); }; }}} 构造函数的名字与类名相同,没有返回类型。它利用特定的值构造对象,把对象初始化为一个特定的状态。 {{{#!cplusplus Clock::Clock( int h, int m, int s) { hour = h; minute = m; second = s; } }}} 带构造函数的对象定义 {{{#!cplusplus int main() { Clock now; //错误 Clock getup(6, 30, 30); Clock now = Clock(9, 21, 20); Clock *pC = new Clock(23, 30, 25); } |
class clock{ int hour, minute, second; public: /*构造函数,函数名与类名相同,没有返回类型*/ clock(){ hour = minute = second = 0; } void show_time() { cout << hour << minute << second; } }; int main() { clock c1; // 定义对象,定义的同时并且初始化 c1.show_time(); } }}} 这种没有参数的构造函数称作默认构造函数(default constructor)。 如果要在定义对象的同时将对象初始化成某一个给定的值,则可以给构造函数添加参数: {{{#!cplusplus class clock{ int hour, minute, second; public: clock(int h, int m, int s) { hour = h; minute = m; second = s; } void show_time() { cout << hour << minute << second; } }; int main() { clock c1(10, 10, 30); c1.show_time(); clock c2; //错误,缺少参数不能完成初始化 c2.show_time(); } }}} 构造函数也可以定义在类外: {{{#!cplusplus class clock{ int hour, minute, second; public: clock(int h, int m, int s); }; clock::clock( int h, int m, int s) { hour = h; minute = m; second = s; } int main() { clock now; //错误 clock sleep(22, 30); clock getup(6, 30, 30); clock *pC = new clock(23, 30, 25); clock now = clock(9, 21, 20); } }}} 构造函数初始化列表 {{{#!cplusplus class clock{ int hour, minute, second; public: clock(int h, int m, int s) : hour(h), minute(m), second(s) { } void show_time() { cout << hour << minute << second; } }; }}} 初始化列表用在类中内嵌对象的情况: {{{#!cplusplus class world_clock { clock c; int timezone; public: world_clock(int hour, int minute, int second, int t) :c(hour, minute, second), timezone(t) { } }; |
行号 236: | 行号 469: |
class Clock { | class clock { |
行号 239: | 行号 472: |
Clock(int h, int m=0, int s=0); Clock(); // default constructor默认构造函数 Clock(const char *); void Clock (/*...*/); // error |
clock(int h, int m=0, int s=0); clock(); // default constructor默认构造函数 clock(const char *s); void clock (/*...*/); // error |
行号 248: | 行号 481: |
Clock now; //默认构造now对象call default constructor Clock getup(6, 30, 30); Clock now = Clock(9, 21); Clock *pC = new Clock("23:30:0"); } }}} 一个类要能够默认构造对象,需要:这个类一个构造函数都没有写(系统会自动生成一个默认构造函数),或者至少写有一个默认构造函数。一个所有参数具有默认参数的构造函数也是默认构造函数,比如 {{{#!cplusplus class Clock { |
clock now; //默认构造now对象call default constructor clock getup(6, 30, 30); clock early(6); clock *pC = new clock("23:30:0"); clock now = clock(9, 21); } }}} 一个类中一个构造函数都没有写,编译器会自动生成一个默认构造函数。 {{{#!cplusplus class clock { |
行号 259: | 行号 493: |
public: Clock(int h =0, int m = 0, int s=0); }; |
}; int main() { clock c1; //没有初始化 } }}} 自动生成的构造函数可以调用内嵌类型的默认构造函数(如果有默认构造函数的话)。 {{{#!cplusplus class student { string name; string num; bool gender; public: void display() { cout << name << num << gender; } }; int main() { student stud; stud.display(); } }}} 一个所有参数具有默认参数的构造函数也是默认构造函数,比如 {{{#!cplusplus class clock { int hour, minute, second; public: clock(int h =0, int m = 0, int s=0); }; int main() { clock c1; } |
行号 266: | 行号 531: |
class Clock { int hour, minute, second; public: ~Clock(); }; }}} 析构函数的名字为类名前加~,没有返回类型和参数。析构函数不能重载。例如: {{{#!cplusplus int main() { Clock d1(4, 11, 1); |
class clock { int hour, minute, second; public: ~clock() { //作一些资源释放操作 } }; }}} 析构函数的名字为类名前加~,没有返回类型和参数。析构函数不能重载。析构函数会被自动调用,例如: {{{#!cplusplus int main() { clock d1(4, 11, 1); |
行号 277: | 行号 544: |
Clock *d2 = new Clock(9, 9, 9); Clock d3(8, 10,0); |
clock *d2 = new clock(9, 9, 9); clock d3(8, 10,0); |
行号 282: | 行号 549: |
Clock d4; | clock d4; |
行号 286: | 行号 553: |
析构函数一般用在需要资源释放的地方,比如: {{{#!cplusplus class mystring{ char *s; public: mystring(char *str) { s = new char[strlen(str)+1]; strcpy(s, str); } ~mystring() { delete[] s; } }; int main() { mystring s("Hello"); } }}} |
|
行号 289: | 行号 575: |
Clock *d1 = new Clock ; Clock *d2 = new Clock ( 10, 11, 1); Clock *d3 = new Clock [ 1000 ]; Clock *d4 = (Clock *)malloc( sizeof(Clock) ); |
clock *d1 = new clock ; clock *d2 = new clock ( 10, 11, 1); clock *d3 = new clock [ 1000 ]; clock *d4 = (clock *)malloc( sizeof(clock) ); // 没有初始化 |
行号 295: | 行号 581: |
delete[ ] d3; | delete[] d3; |
行号 303: | 行号 589: |
Clock cls[10]; Clock *p = new Clock[10]; } }}} |
clock cls[10]; clock *p = new clock[10]; } }}} |
行号 311: | 行号 598: |
对象在两种情况下发生复制:赋值和拷贝构造。拷贝构造是指创建一个对象,创建的同时让它的值与另一个已存在的对象一模一样。赋值是复制另一个同类型的值给已存在的对象,使它变成新的值。比如 {{{#!cplusplus void f( Date d ); int main() { Date today; //默认构造函数 Date d = today; //拷贝构造 Date s( today ); //拷贝构造 d = another_day; //赋值 f(s); //拷贝构造 |
对象的复制分为两种情况:赋值和拷贝构造。拷贝构造是指创建一个新对象,创建的同时让新对象的值与另一个已存在的对象一模一样。赋值是复制一个对象的值给另一个已存在的对象,使两个对象的值一样。比如 {{{#!cplusplus void display( clock d ) { //... } int main() { clock now; //默认构造函数 clock d = now; //拷贝构造 clock s( now ); //拷贝构造 d = s; //赋值 display(s); //拷贝构造 |
行号 324: | 行号 613: |
拷贝构造的工作由拷贝构造函数完成。如果自己没有编写拷贝构造函数,编译器会自动生成一个拷贝构造函数,用于完成缺省的拷贝构造的功能:用源对象的所有数据成员逐一拷贝构造目标对象的相应成员。我们可以自定义拷贝构造函数去改写默认的拷贝构造函数。 {{{#!cplusplus class Clock { |
拷贝构造的工作由一个特殊的构造函数拷贝构造函数完成。如果一个类中没有编写拷贝构造函数,编译器会自动生成一个拷贝构造函数,用于完成默认的拷贝构造的功能:用源对象的所有数据成员逐一拷贝构造目标对象的相应成员。我们可以自定义拷贝构造函数去改写默认的拷贝构造函数。比如: {{{#!cplusplus class clock { |
行号 329: | 行号 618: |
Clock(Clock& c) { | clock(int h, int m, int s) { hour = h; minute = m; second = s; } //这个拷贝构造函数实现的功能与编译器自动声成的相同,可以省略 clock(clock& c) { |
行号 331: | 行号 626: |
minite = c.minute; second = 0; } }; }}} 何时需要自定义拷贝构造函数、析构函数?当涉及到内存管理时,一个类中有指针成员,指向动态分配的空间,那么通常需要写一组拷贝构造函数、析构函数和赋值操作符。写一个类Array,模拟数组的功能,元素是int类型,数组的大小可以是变量,增加检查越界的功能。即可以这样使用: {{{#!cplusplus int main() { Array arr(10); // 相当于int arr[10]; |
minute = c.minute; second = c.second; } }; int main() { clock c; clock d(c); //拷贝构造d,其值与c一样。 } }}} 何时需要自定义拷贝构造函数、析构函数?当涉及到内存管理时,一个类中有指针成员,指向动态分配的空间,那么通常需要写一组拷贝构造函数、析构函数和赋值操作符。例如: {{{#!cplusplus class mystring{ char *s; public: mystring(char *str) { s = new char[strlen(str)+1]; strcpy(s, str); } ~mystring() { delete[] s; } mystring(mystring &str) { s = new char[strlen(str.s)+1]; strcpy(s, str.s); } }; int main() { mystring s("Hello"); mystring s2(s); } }}} 再例如:写一个类array,模拟数组的功能,元素是int类型,数组的大小可以是变量,增加检查越界的功能。即可以这样使用: {{{#!cplusplus int main() { array arr(10); // 相当于int arr[10]; |
行号 346: | 行号 671: |
class Array { | class array { |
行号 351: | 行号 676: |
Array(int s) { | array(int s) { |
行号 355: | 行号 680: |
~Array() { | ~array() { |
行号 370: | 行号 695: |
Array arr( 10 ); | array arr( 10 ); |
行号 372: | 行号 697: |
Array arr2(arr); // error here | array arr2(arr); // error here |
行号 378: | 行号 703: |
class Array { | class array { |
行号 383: | 行号 708: |
Array(int s) { | array(int s) { |
行号 387: | 行号 712: |
~Array() { | ~array() { |
行号 396: | 行号 721: |
Array( Array &a) { | array( array &a) { |
行号 441: | 行号 766: |
public: | |
行号 443: | 行号 767: |
public: point(double x0, double y0) { x = x0; y = y0; } }; class line { point start, end; public: line(double x0, double y0, double x1, double y1) { start.x = x0; start.y = y0; end.x = x1; end.y = y1; } }; }}} 这个程序遇到编译错误。一是因为point类的x,y是私有的,在line类的构造函数中访问point的x,y是错误的。二是构造line中内含的两个point对象start,end需要参数,而在line的构造函数中没有给出这些参数。解决办法:使用构造函数初始化列表 {{{#!cplusplus class point{ private: double x, y; public: point(double x0, double y0) { x = x0; y = y0; cout << "point " << x << y << "initialized!" << endl; } }; class line { private: point start, end; public: line(double x0, double y0, double x1, double y1) : start(x0, y0), end(x1, y1) { cout << "line initializing" << endl; } }; int main() { line l(1,2,3,4); } }}} 在line构造函数的初始化列表中,给start、end成员的构造函数传递参数完成它们的初始化。 如果内嵌的类有默认构造函数,则初始化列表可以省略。 {{{#!cplusplus class point{ private: double x, y; public: |
|
行号 451: | 行号 827: |
public: | private: |
行号 453: | 行号 829: |
public: | |
行号 455: | 行号 832: |
start.x = x0; start.y = y0; end.x = x1; end.y = y1; } }; }}} 从这个程序的运行结果可以看出,在line类的构造函数执行前,start和end对象已经构造完成。point类没有默认构造函数,line不能构造。如果x、y是私有的,那么在line构造函数中不能对他们进行赋值。解决办法:使用构造函数初始化列表 {{{#!cplusplus class point{ private: double x, y; public: point(double x0, double y0) { x = x0; y = y0; cout << "point " << x << y << "initialized!" << endl; } }; class line { private: point start, end; public: line(double x0, double y0, double x1, double y1) : start(x0, y0), end(x1, y1) { cout << "line initializing" << endl; } }; }}} 在line构造函数的初始化列表中,给start、end成员的构造函数传递参数完成它们的初始化。 |
} }; int main() { line l(1,2,3,4); } }}} |
行号 498: | 行号 852: |
Clock time; // no default construct Clock &t; // reference member |
clock time; // no default construct clock &t; // reference member |
行号 530: | 行号 884: |
== 静态成员 == 类的一般数据成员,每个对象都有这样一组数据成员,它们的空间是独立的。C++的类还有一种特殊的成员,它的空间是这个类的所有对象共享的,称为类静态数据成员 {{{#!cplusplus class task { public: static unsigned n; //声明静态成员n int m; }; unsigned task::n; //定义静态成员n int main() { task t1, t2; t1.n = 10; cout << t2.n; } }}} 这里n的空间在整个程序里面只有一个,t1,t2共享同一个n。 静态数据成员需要声明和定义。初始化不是在构造函数中进行,而是在定义的时候进行。访问静态数据成员可以和一般成员一样,通过对象名来访问,也可以不通过对象直接用类名来访问。 {{{#!cplusplus class task { public: task() : x(n) { n++; } ~task() { n--; } int x; static unsigned n; }; unsigned task::n = 1;// 初始化 int main() { cout << task::n << endl; task s1; cout << s1.x << s1.n << endl; task s2; cout << s1.x << s1.n << endl; cout << s2.x << s2.n << endl; cout << task::n << endl; } }}} 静态成员函数:只能访问静态成员(包括静态数据成员和静态成员函数)的成员函数 {{{#!cplusplus class task { private: static unsigned n; public: static unsigned get_count() { return n; } }; unsigned task::n = 0; int main() { cout << task::get_count(); task t; cout << t.get_count(); } }}} 静态成员用法举例:写一个类Singleton,这个类只能存在一个对象。 {{{#!cplusplus class singleton{ public: static singleton &instance() { static singleton s; return s; } private: singleton() { /*...*/ } singleton(singleton&){ /*...*/ } }; }}} 统计所有学生的平均成绩: {{{#!cplusplus class student { string name; string num; float score; static float sum; static int count; public: student(string na, string nu, float sc) : name(na), num(nu), score(sc) { count ++; sum += score; } static float average() { return sum / count; } }; float student::sum = 0; int student::count = 0; int main() { cout << student::average(); student a("Jack", "0511001", 59); cout << a.average() << Student::average(); student b("Lisa", "0511002", 99); cout << a.average() << b.average() << Student::average(); } }}} == 友元 == 友元提供了让其它函数或类直接访问私有成员的途径。友元函数是能够直接访问类的私有成员的函数。比如要写一个函数isequal比较两个点是否相同: {{{#!cplusplus class point { private: int x, y; public: friend bool isequal(point a, point b); }; bool isequal(point a, point b) { return a.x == b.x && a.y == b.y; } int main() { point a, b; // initialize cout << isequal(a, b); } }}} 实际上不用友员也可以实现同样的功能,只要将x,y变成公有或者提供共有的函数访问他们就可以了: {{{#!cplusplus class point { private: int x, y; public: int &get_x() { return x; } int &get_y() { return y; } }; bool isequal(Point a, Point b) { return a.get_x() == b.get_x() && a.get_y() == b.get_y(); } int main() { point a, b; // initialize cout << isequal(a, b); } }}} 友元类:一个类是另一个类的友员,则一个类的所有成员函数都可以访问另一个类的所有私有成员 {{{#!cplusplus class point { int x, y; friend class rectangle; // Rectangle是Point的友元类,Rectangle类的成员函数可以访问Point类的私有成员 }; class rectangle { point p1, p2; public: void display() { cout << p1.x << p1.y << p2.x << p2.y; } bool in(point p) { return p.x >= p1.x && p.x < p2.x && p.y >= p1.y && p.y < p2.y; } }; int main() { rectangle rect; // initialize rect.display(); } }}} 注意:友元关系是单向的,并不可传递。 == const对象 == 定义对象前面加const,表示对象不能改变 {{{#!cplusplus class clock{ int hour, minute, second; public: clock(int h, int m, int s) : hour(h), minute(m), second(s) { } void set_time(int h, int m, int s) { hour = h; minute = m; second = s; } void show_time() { cout << hour << minute << second; } }; int main() { const int i = 10; cout << i*10; // ok i++; // error! const clock t(10, 30, 20); cout << t.second; // ok t.second = 10; // error t.set_time(10, 20, 10); // error! t.show_time(); // error! } }}} const对象不能调用普通的成员函数,即使这个函数并不修改数据成员。对于不修改数据成员的成员函数,可以在声明后面加const,表示这个成员函数不修改数据成员,这样的函数可以被const对象调用。 {{{#!cplusplus class clock{ int hour, minute, second; public: clock(int h, int m, int s) : hour(h), minute(m), second(s) { } void set_time(int h, int m, int s) { hour = h; minute = m; second = s; } void show_time() const { cout << hour << minute << second; } }; int main() { const int i = 10; cout << i*10; // ok i++; // error! const clock t(10, 30, 20); cout << t.second; // ok t.second = 10; // error t.set_time(10, 20, 10); // error! t.show_time(); // ok } }}} const成员函数可以被const对象调用,也可以被普通对象调用。 {{{#!cplusplus int main() { const clock time(10, 20, 30); time.show_time(); clock now(10, 10, 10); time.show_time(); } }}} 常成员函数与非常成员函数可以重载 {{{#!cplusplus class array { private: int *p, size; public: array(int s) { size = s; p = new int [ size ]; } ~array() { delete[ ] p; } int & at( int i) { if( i < size) return p[i]; else throw out_of_range(); } int at( int i) const { if( i < size) return p[i]; else throw out_of_range(); } }; int main() { array a(10); a.at(5) = 5; const array b(5); b.at(5) = 5; // error } }}} == this指针 == {{{#!cplusplus class clock { int second, minute, hour; public: clock(int h, int m, int s) : hour(h), minute(m), second(s) { } void show_time() { cout << second << minute << hour; //cout << this->second << this->minute << this->hour; } }; int main() { clock t1(10, 20, 30), t2(20, 30, 10); t1.show_time(); t2.show_time(); } }}} {{{#!cplusplus class clock { public: clock &set_second(int n); clock &set_minute(int n); clock &set_hour(int n); }; clock &clock::set_second(int n) { second = n; return *this; } int main() { clock time(1999, 9, 9); time.set_second(20); time.set_minute(30) time.set_hour(10); time.set_second(20).set_minute(30).set_hour(10); } }}} this有两种类型:以clock类为例,clock* const和const clock* const {{{#!cplusplus class clock { public: void show_time() const { cout << second << minute << hour; second++; //error this类型为const Date *this clock *p = (clock*)(this); // 或者clock *p = const_cast<clock*>(this); p->second++; //ok } }; }}} |
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 void display(complex a);
9
10 int main() {
11 complex x, y;
12 x.real = 10.0;
13 x.imag = 20.0;
14 y.real = 1.0;
15 y.imag = -2.0;
16 complex z = add(x, y);
17 display(z);
18 }
另一个例子:
1 struct clock {
2 int second;
3 int minute;
4 int hour;
5 };
6 void set_time( clock *c, int h, int m, int s) {
7 c->second = s;
8 c->minute = m;
9 c->hour = h;
10 }
11 void show_time(const clock *c) {
12 cout << c->hour << c->minute << c->second;
13 }
14
15 int main() {
16 clock c;
17 set_time(&c, 10, 10, 30);
18 show_time(&c);
19 }
再一个例子:
1 #define MALE true
2 #define FEMALE false
3 struct student{
4 char num[20];
5 char name[10];
6 bool gender;
7 };
8 void set(student &s, char id[], char n[], bool g){
9 strcpy(s.num, id);
10 strcpy(s.name, n);
11 s.gender = g;
12 }
13 void display(student &s) {
14 cout << s.num << s.name << (s.gender?"male":"female");
15 }
16 int main() {
17 student stud1, stud2;
18 // initialize
19 set(stud1, "110101", "Rose", FEMALE);
20 set(stud2, "110102", "JACK", MALE);
21 display(stud1);
22 display(stud2);
23 }
2. C++的类
C语言中,表示数据的结构体和操作这些结构体的函数是分开的。或者说数据结构和操作它的算法是分开的。这样数据和操作之间的关系不是很清晰。C++的做法是在C语言的基础上更进一步,将数据与操作这组数据的函数结合在一起,构成类(class)
C++中类的定义可以用struct或者用class。函数与数据结合在一起,逻辑关系更加明确。定义在类中的函数又被称为方法、成员函数。成员函数可以直接访问类(结构体)中的数据成员。C++类中函数的定义,函数体可以直接写在类的内部,写在头文件中:
1 struct clock {
2 int hour, minute, second;
3 void set_time(int h, int m, int s) {
4 hour = h;
5 minute = m;
6 second = s;
7 }
8 void show_time() {
9 cout << hour << minute << second;
10 }
11 };
12
13 int main() {
14 clock c1, c2;
15 c1.set_time(10, 10, 30);
16 c2.set_time(18, 00, 00);
17 c1.show_time();
18 c2.show_time();
19 }
也可以分开定义,比如:
1 //clock.h头文件:
2 struct clock {
3 int hour, minute, second;
4 void set_time(int h, int m, int s);
5 void show_time();
6 };
7
8 //clock.cpp源文件:
9 void clock::set_time(int h, int m, int s) {
10 hour = h;
11 minute = m;
12 second = s;
13 }
14 void clock::show_time() {
15 cout << hour << minute << second;
16 }
17
18 // main.cpp源文件
19 int main() {
20 clock c1, c2;
21 c1.set_time(10, 10, 30);
22 c2.set_time(18, 00, 00);
23 c1.show_time();
24 c2.show_time();
25 }
另一个例子:
1 const bool MALE = true;
2 const bool FEMALE = false;
3 struct student{
4 string num;
5 string name;
6 bool gender;
7 void set(string id, string n, bool g) {
8 num = id;
9 name = n;
10 gender = g;
11 }
12 void diplay() {
13 cout << num << name << (gender?"male":"female");
14 }
15 };
16 int main() {
17 student stud1, stud2;
18 stud1.set("110101", "Rose", FEMALE);
19 stud2.set("110102", "Jack", MALE);
20 stud1.display();
21 stud2.display();
22 }
对象的定义和访问方式类似于结构体
在堆空间分配和访问对象
定义对象数组
成员函数和普通函数一样可以内联。函数体写在类定义内部的成员函数,默认就是内联的。
类外定义函数体的成员函数,要定义成内联需要加inline关键字,并把函数体写在头文件中
成员函数也可以重载
成员函数也可以有缺省参数。缺省值写在声明中,而不是定义中。
3. 类的访问权限
在前面的clock类中,我们发现类中的数据成员hour、minute、second不需要被clock类的使用者直接访问。如果直接访问,还可能会带来副作用。在C++中增加了对类的成员的访问权限的控制,把成员分为public和private等。
public部分定义了类的外部接口,可供类的使用者调用。private部分隐藏了类的具体实现,由类的实现者实现,不需要使用者关心。这就是‘’‘封装’‘’。private和public为新增的关键字。
struct与class的访问权限的区别:struct默认public,class默认为private
另一个例子:
1 class student{
2 private:
3 string num;
4 string name;
5 bool gender;
6 public:
7 void display() {
8 cout << num << name << (gender?"male":"female");
9 }
10 void setnum(string n) {
11 num = n;
12 }
13 void setname(char n) {
14 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 }
并非所有数据成员都必须是private,并非所有成员函数都必须是公有的。
1 class clock {
2 private: //只能在类内访问
3 int hour, minute, second;
4 public: //可以在类外访问
5 void add(int s) {
6 for(int i = 0; i < s; i++)
7 inc();
8 }
9 private:
10 void inc() {
11 second++;
12 if(second == 60) {
13 second = 0;
14 minute ++;
15 if(minute == 60) {
16 minute = 0;
17 hour++;
18 if(hour == 24)
19 hour = 0;
20 }
21 }
22 }
23 };
4. 构造与析构
定义变量的同时完成初始化(resource acquisition is initialization)是一种好的编程习惯,可以避免错误。
为避免偶然使用没有初始化的对象的错误,可以在类中定义一个特殊的函数——构造函数。它在对象被定义时,就被自动调用,以确保完成初始化。
这种没有参数的构造函数称作默认构造函数(default constructor)。
如果要在定义对象的同时将对象初始化成某一个给定的值,则可以给构造函数添加参数:
1 class clock{
2 int hour, minute, second;
3 public:
4 clock(int h, int m, int s) {
5 hour = h;
6 minute = m;
7 second = s;
8 }
9 void show_time() {
10 cout << hour << minute << second;
11 }
12 };
13
14 int main() {
15 clock c1(10, 10, 30);
16 c1.show_time();
17 clock c2; //错误,缺少参数不能完成初始化
18 c2.show_time();
19 }
构造函数也可以定义在类外:
1 class clock{
2 int hour, minute, second;
3 public:
4 clock(int h, int m, int s);
5 };
6 clock::clock( int h, int m, int s) {
7 hour = h;
8 minute = m;
9 second = s;
10 }
11
12 int main() {
13 clock now; //错误
14 clock sleep(22, 30);
15 clock getup(6, 30, 30);
16 clock *pC = new clock(23, 30, 25);
17 clock now = clock(9, 21, 20);
18 }
构造函数初始化列表
初始化列表用在类中内嵌对象的情况:
构造函数可以重载,也可以有缺省参数:
各个构造函数所需要参数不同,但构造函数都没有返回类型。在定义对象时,会根据传递的参数来选择一个特定的构造函数初始化对象。
一个类中一个构造函数都没有写,编译器会自动生成一个默认构造函数。
自动生成的构造函数可以调用内嵌类型的默认构造函数(如果有默认构造函数的话)。
一个所有参数具有默认参数的构造函数也是默认构造函数,比如
对象被摧毁时,一个成员函数也会自动被调用,这个函数称为称为析构函数,一般完成资源的释放工作
析构函数的名字为类名前加~,没有返回类型和参数。析构函数不能重载。析构函数会被自动调用,例如:
析构函数一般用在需要资源释放的地方,比如:
new会自动调用构造函数,而malloc不能。delete会自动调用析构函数,而free不能。
要定义对象数组,并且没有初始化,那么要求该类可以默认构造对象。比如:
5. 对象的拷贝构造
对象的复制分为两种情况:赋值和拷贝构造。拷贝构造是指创建一个新对象,创建的同时让新对象的值与另一个已存在的对象一模一样。赋值是复制一个对象的值给另一个已存在的对象,使两个对象的值一样。比如
拷贝构造的工作由一个特殊的构造函数拷贝构造函数完成。如果一个类中没有编写拷贝构造函数,编译器会自动生成一个拷贝构造函数,用于完成默认的拷贝构造的功能:用源对象的所有数据成员逐一拷贝构造目标对象的相应成员。我们可以自定义拷贝构造函数去改写默认的拷贝构造函数。比如:
1 class clock {
2 int hour, minute, second;
3 public:
4 clock(int h, int m, int s) {
5 hour = h;
6 minute = m;
7 second = s;
8 }
9 //这个拷贝构造函数实现的功能与编译器自动声成的相同,可以省略
10 clock(clock& c) {
11 hour = c.hour;
12 minute = c.minute;
13 second = c.second;
14 }
15 };
16
17 int main() {
18 clock c;
19 clock d(c); //拷贝构造d,其值与c一样。
20 }
何时需要自定义拷贝构造函数、析构函数?当涉及到内存管理时,一个类中有指针成员,指向动态分配的空间,那么通常需要写一组拷贝构造函数、析构函数和赋值操作符。例如:
1 class mystring{
2 char *s;
3 public:
4 mystring(char *str) {
5 s = new char[strlen(str)+1];
6 strcpy(s, str);
7 }
8 ~mystring() {
9 delete[] s;
10 }
11 mystring(mystring &str) {
12 s = new char[strlen(str.s)+1];
13 strcpy(s, str.s);
14 }
15 };
16 int main() {
17 mystring s("Hello");
18 mystring s2(s);
19 }
再例如:写一个类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 };
组合对象的构造
这个程序遇到编译错误。一是因为point类的x,y是私有的,在line类的构造函数中访问point的x,y是错误的。二是构造line中内含的两个point对象start,end需要参数,而在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 };
21 int main() {
22 line l(1,2,3,4);
23 }
在line构造函数的初始化列表中,给start、end成员的构造函数传递参数完成它们的初始化。
如果内嵌的类有默认构造函数,则初始化列表可以省略。
1 class point{
2 private:
3 double x, y;
4 public:
5 point(double x0 = 0, double y0 = 0) {
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 cout << "line initializing" << endl;
18 }
19 };
20
21 int main() {
22 line l(1,2,3,4);
23 }
初始化表还能够解决其它一些类型的成员的初始化问题,比如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++的类还有一种特殊的成员,它的空间是这个类的所有对象共享的,称为类静态数据成员
这里n的空间在整个程序里面只有一个,t1,t2共享同一个n。
静态数据成员需要声明和定义。初始化不是在构造函数中进行,而是在定义的时候进行。访问静态数据成员可以和一般成员一样,通过对象名来访问,也可以不通过对象直接用类名来访问。
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 = 1;// 初始化
9
10 int main() {
11 cout << task::n << endl;
12 task s1;
13 cout << s1.x << s1.n << endl;
14 task s2;
15 cout << s1.x << s1.n << endl;
16 cout << s2.x << s2.n << endl;
17 cout << task::n << endl;
18 }
静态成员函数:只能访问静态成员(包括静态数据成员和静态成员函数)的成员函数
静态成员用法举例:写一个类Singleton,这个类只能存在一个对象。
统计所有学生的平均成绩:
1 class student {
2 string name;
3 string num;
4 float score;
5 static float sum;
6 static int count;
7 public:
8 student(string na, string nu, float sc)
9 : name(na), num(nu), score(sc) {
10 count ++;
11 sum += score;
12 }
13 static float average() {
14 return sum / count;
15 }
16 };
17
18 float student::sum = 0;
19 int student::count = 0;
20
21 int main() {
22 cout << student::average();
23 student a("Jack", "0511001", 59);
24 cout << a.average() << Student::average();
25 student b("Lisa", "0511002", 99);
26 cout << a.average() << b.average() << Student::average();
27 }
8. 友元
友元提供了让其它函数或类直接访问私有成员的途径。友元函数是能够直接访问类的私有成员的函数。比如要写一个函数isequal比较两个点是否相同:
实际上不用友员也可以实现同样的功能,只要将x,y变成公有或者提供共有的函数访问他们就可以了:
友元类:一个类是另一个类的友员,则一个类的所有成员函数都可以访问另一个类的所有私有成员
1 class point {
2 int x, y;
3 friend class rectangle; // Rectangle是Point的友元类,Rectangle类的成员函数可以访问Point类的私有成员
4 };
5 class rectangle {
6 point p1, p2;
7 public:
8 void display() {
9 cout << p1.x << p1.y << p2.x << p2.y;
10 }
11 bool in(point p) {
12 return p.x >= p1.x && p.x < p2.x && p.y >= p1.y && p.y < p2.y;
13 }
14 };
15
16 int main() {
17 rectangle rect;
18 // initialize
19 rect.display();
20 }
注意:友元关系是单向的,并不可传递。
9. 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 set_time(int h, int m, int s) {
9 hour = h;
10 minute = m;
11 second = s;
12 }
13 void show_time() {
14 cout << hour << minute << second;
15 }
16 };
17 int main() {
18 const int i = 10;
19 cout << i*10; // ok
20 i++; // error!
21 const clock t(10, 30, 20);
22 cout << t.second; // ok
23 t.second = 10; // error
24 t.set_time(10, 20, 10); // error!
25 t.show_time(); // error!
26 }
const对象不能调用普通的成员函数,即使这个函数并不修改数据成员。对于不修改数据成员的成员函数,可以在声明后面加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 set_time(int h, int m, int s) {
9 hour = h;
10 minute = m;
11 second = s;
12 }
13 void show_time() const {
14 cout << hour << minute << second;
15 }
16 };
17 int main() {
18 const int i = 10;
19 cout << i*10; // ok
20 i++; // error!
21 const clock t(10, 30, 20);
22 cout << t.second; // ok
23 t.second = 10; // error
24 t.set_time(10, 20, 10); // error!
25 t.show_time(); // ok
26 }
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 }
10. this指针
1 class clock {
2 int second, minute, hour;
3 public:
4 clock(int h, int m, int s)
5 : hour(h), minute(m), second(s)
6 {
7 }
8 void show_time() {
9 cout << second << minute << hour;
10 //cout << this->second << this->minute << this->hour;
11 }
12 };
13 int main() {
14 clock t1(10, 20, 30), t2(20, 30, 10);
15 t1.show_time();
16 t2.show_time();
17 }
1 class clock {
2 public:
3 clock &set_second(int n);
4 clock &set_minute(int n);
5 clock &set_hour(int n);
6 };
7 clock &clock::set_second(int n) {
8 second = n;
9 return *this;
10 }
11 int main() {
12 clock time(1999, 9, 9);
13 time.set_second(20);
14 time.set_minute(30)
15 time.set_hour(10);
16 time.set_second(20).set_minute(30).set_hour(10);
17 }
this有两种类型:以clock类为例,clock* const和const clock* const