第1章 C++基础
1.1 简单C++程序 [例Ex_Simple] 一个简单的C++程序 #include <iostream.h> void main() { double r, area; // 声明变量 cout<<"输入圆的半径:"; // 显示提示信息 cin>>r; // 从键盘上输入r的值 area = 3.14159 * r * r; // 计算面积 cout<<“圆的面积为:”<<area<<“\n”;// 输出面积 } 该程序经编译、连接、运行后,屏幕上显示: 输入圆的半径: 此时等待用户输入,当输入10并按Enter键后,屏幕显示: 圆的面积为:314.159 Press any key to continue 这就是程序运行的结果。
1.1 简单C++程序 [例Ex_Root] 一个求解一元二次方程的C++程序 #include <iostream.h> #include <math.h> int GetRoot(float a, float b, float c, double *root); /* 声明一个全局函数 */ void main() { float a = 2.0, b = 6.0, c = 3.0; // 定义并初始化变量 double root[2]; int n = GetRoot(a, b, c, roo // 调用函数 if (n<1) cout<<"方程无根!"; else { cout<<"方程有 "<<n<<" 根:\n"; for (int i=0; i<n; i++) // 循环输出所有的根 cout<<"根"<<i+1<<": "<<root[i]<<"\t"; } cout<<endl;
1.1 简单C++程序 // 求一元二次方程的根,函数返回根的个数 int GetRoot(float a, float b, float c, double *root) { double delta, deltasqrt; delta = b*b - 4.0 * a * c; if (delta<0.0) return 0; // 无根 deltasqrt = sqrt(delta); if (a!=0.0) root[0] = (-b + deltasqrt)/(2.0 * a); root[1] = (-b - deltasqrt)/(2.0 * a); } else if (b!=0.0) root[0] = root[1] = -c/b; else return 0; } if (root[0] == root[1]) return 1; else return 2 方程有 2 根: 根1: -0.633975 根2: -2.36603
1.2.1 从结构到类 [例Ex_StructToClass] 从结构到类的示例 #include <iostream.h> 1.2.1 从结构到类 [例Ex_StructToClass] 从结构到类的示例 #include <iostream.h> struct STUSCORE { char strName[12]; // 姓名 char strStuNO[9]; // 学号 float fScore[3]; // 三门课程成绩 }; float GetAverage(STUSCORE one) // 计算平均成绩 { return (float)((one.fScore[0] + one.fScore[1] + one.fScore[2])/3.0); } void main() STUSCORE one={"LiMing", "21020501", {80,90,65}}; cout<<one.strName<<" 的平均成绩为: "<<GetAverage(one)<<"\n"; 运行结果如下: LiMing 的平均成绩为: 78.3333
1.2.2 类的定义 类一般分为声明部分和实现部分。 C++中定义类的一般格式如下: 1.2.2 类的定义 类一般分为声明部分和实现部分。 C++中定义类的一般格式如下: class <类名> { private: [<私有数据和函数>] public: [<公有数据和函数>] }; <各个成员函数的实现> 类体 class是定义类的关键字,class的后面是用户定义的类名,用 大写的C字母开始的标识符作为类名,C用来表示类(Class),以与对象、函数及其他数据类型相区别。类中的数据和函数是类的成员,称为数据成员和成员函数。
1.2.2 类的定义 CStuScore类包含了SetScore和GetAverage成员函数,分别用来输入成绩和返回计算后的平均成绩: 1.2.2 类的定义 CStuScore类包含了SetScore和GetAverage成员函数,分别用来输入成绩和返回计算后的平均成绩: class CStuScore { public: // 公有类型声明 char strName[12]; // 姓名 char strStuNO[9]; // 学号 void SetScore(float s0, // 成员函数:设置三门课成绩 fScore[0] = s0; fScore[1] = s1; fScore[2] = s2; } float GetAverage(); private: // 私有类型声明 float fScore[3]; // 三门课程成绩 }; // 注意分号不能省略 float CStuScore::GetAverage() return (float)((fScore[0] + fScore[1] + fScore[2])/3.0); 类CStuScore中,成员函数SetScore是在类体中定义,GetAverage是类的外 部定义,注意两者的区别。
1.2.2 类的定义 定义类时还应注意: (1) 在“public:”或“private:”后面成员都是公有或私有的,直到下一个“public:”或“private:”出现为止。 (2)关键字public和private在类中出现多次,前后的顺序没有关系。 (3)除了public和private外,关键字protected也可修饰成员的类型。 (4)数据成员的类型可以是任意的,包含整型、浮点型、字符型、数组、指针等。 (5)将类单独存放在一个文件中或将类的声明放在.h文件中而将成员函数的实现放在与.h文件同名的.cpp文件中。 Visual C++ 6.0为用户创建的应用程序框架中都是将各个类以.h和同名的.cpp文件来组织的。
1.2.3 对象的定义 一个类定义后,就可以定义该类的对象,如下面的格式: <类名> <对象名列表> 1.2.3 对象的定义 一个类定义后,就可以定义该类的对象,如下面的格式: <类名> <对象名列表> 类名是用户已定义过的类的标识符,对象名可以有一个或多个,多个时要用 逗号分隔。 CStuScore one, *two, three[2]; 一个对象就是该对象的类所定义的成员,引用(访问)时可用下列方式: <对象名>.<成员名> <对象名>.<成员名>(<参数表>) 前者表示引用数据成员,后者表示引用成员函数。“.”是成员运算符, 用引用对象成员。 one.strName, three[0].GetAverage(); 对于指针对象的成员引用可用下列方式: <对象指针名>-><成员名> <对象指针名>-><成员名>(<参数表>) “->”也是一个成员运算符,与“.”运算符的区别是:“->”用来访问指针对象的成员,而“.”用来访问一般对象的成员。下面的两种表示是等价的: (*<对象指针名>).<成员名> 成员函数也适用,
1.3.1 构造函数 C++规定:构造函数必须与相应的类同名构造函数不能指定函 数返回值的类型,也不能指定为void类型。 1.3.1 构造函数 C++规定:构造函数必须与相应的类同名构造函数不能指定函 数返回值的类型,也不能指定为void类型。 class CStuScore { public: CStuScore(char str[12]) // 第一个构造函数 { strcpy(strName, str); } CStuScore(char str[12], char strNO[9]) // 第二个构造函数 strcpy(strStuNO, strNO); char strName[12]; // 姓名 char strStuNO[9]; // 学号class CM ...
1.3.1 构造函数 需要说明的是: (1) 程序中的strcpy是C++的一个库函数,用来复制字符串,使用时需要头文件string.h。 1.3.1 构造函数 需要说明的是: (1) 程序中的strcpy是C++的一个库函数,用来复制字符串,使用时需要头文件string.h。 (2) 在类定义时,如果没有定义任何构造函数,则编译器自动为类生成一个不带任何参数的默认构造函数。对于CStuScore类来说,默认构造函数的形式如下: CStuScore( ) // 默认构造函数的形式 { } (3) 由于构造函数的参数只能在定义对象时指定,因此有: CStuScore oOne("LiMing"); 它是自动调用第一个构造函数,使得strName内容为“LiMing”。若有: CStuScore oTwo;
1.3.2 析构函数 析构函数也要与相应的类同名,并在名称前面加上一个“~”符号。 class CStuScore { public: 1.3.2 析构函数 析构函数也要与相应的类同名,并在名称前面加上一个“~”符号。 class CStuScore { public: ... ~ CStuScore ( ) { } // 析构函数 } 析构函数只有在两种情况下才会被自动调用: (1) 当对象定义在一个函数体中,该函数调用结束后,析构函数被自动调用。 (2) 用new为对象分配动态内存后,当使用delete释放对象时,析构函数被自动调用。
1.3.3 对象成员初始化 为了能对这些对象成员进行初始化,C++允许采用这样的构造函数定义格式: 1.3.3 对象成员初始化 为了能对这些对象成员进行初始化,C++允许采用这样的构造函数定义格式: <类名>::<构造函数名>(形参表):对象1(参数表), 对象2(参数表), …, 对象n(参数表) 需要说明的是: (1) 类的成员对象必须初始化,但不能将成员对象直接在构造函数体内进行初始化 。 (2) 对象成员初始化时,必须有相应的构造函数,且多个对象成员的构造次序不是按初始化成员列表的顺序,而是按各类声明的先后次序进行的。 (3) 成员对象初始化也可在类构造函数定义时进行。 (4) 事实上,成员初始化列表也可用于类中的普通数据成员的初始化。
1.3.4 常类型 常类型是指使用类型修饰符const说明的类型。 常对象 常对象是指对象常量,定义格式如下: 1.3.4 常类型 常类型是指使用类型修饰符const说明的类型。 常对象 常对象是指对象常量,定义格式如下: <类名> const <对象名> 定义常对象时,同样要进行初始化,并且该对象不能再被改变,修饰符const可以放在类名后面,也可以放在类名前面。 常成员函数 使用const关键字进行声明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象。 常成员函数说明格式如下: <类型说明符> <函数名> (<参数表>) const;
1.3.4 常类型 常数据成员 类型修饰符const不仅可以说明成员函数,也可以说明数据成员。 1.3.4 常类型 常数据成员 类型修饰符const不仅可以说明成员函数,也可以说明数据成员。 由于const类型对象必须被初始化,并且不能被改变。在类中声明const数据成员后,只能通过构造函数初始化方式来对常数据成员初始化. 所谓“引用”,实质上是给一个已定义的变量起 一个别名,系统不会为引用类型变量分配内存空间, 只是使引用类型变量与其相关联的变量使用同一 个内存空间。
1.3.5 this指针 [例Ex_This] this指针的使用 private: int x, y; }; void COne::copy(COne &a) { if (this == &a) return; *this = a; } void main() COne one, two(3, 4); one.print(); one.copy(two); #include <iostream.h> class COne { public: COne() { x = y = 0; } COne(int a, int b) { x = a; y = b; void copy(COne &a); // 对象引用作函数参数 void print() cout<<x<<" , "<<y<<endl;
1.3.6 静态成员 静态数据成员和静态成员函数。 静态数据成员 静态数所成员是同一个类中所有对象共享的成,而不是某一对象的成员。 1.3.6 静态成员 静态数据成员和静态成员函数。 静态数据成员 静态数所成员是同一个类中所有对象共享的成,而不是某一对象的成员。 静态数据成员始化须在类的外部进行,与一般数据成员初始化不同,它的格式如下: <数据类型><类名>::<静态数据成员名>=<值> 2. 静态成员函数 静态成员函数和静态数据成员一样,它们都属于类的静态成员,但它们都不是对象的成员。
1.4.1 单继承 1. 公有继承(public) 公有继承的特点是基类的公有成员和保护成员作为派生类 1.4.1 单继承 1. 公有继承(public) 公有继承的特点是基类的公有成员和保护成员作为派生类 的成员时,它们都保持原有的状态,而基类的私有成员仍 然是私有的。 2. 私有继承(private) 私有继承的特点是基类的公有成员和保护成员都作为派生 类的私有成员,并且不能被这个派生类的子类所访问。 3. 保护继承(protected) 保护继承的特点是基类的所有公有成员和保护成员都成为 派生类的保护成员,并且只能被它的派生类成员函数或友 元访问,基类的私有成员仍然是私有的。
1.4.2 派生类的构造函数和析构函数 派生类对象在建立时,先执行基类的构造函数,然后执行派生类的构造函数。 1.4.2 派生类的构造函数和析构函数 派生类对象在建立时,先执行基类的构造函数,然后执行派生类的构造函数。 在对派生类进行初始化时,需要对其基类设置初值,可按下列格式进行: <派生类名>(总参表):<基类1>(参数表1), <基类2>(参数表2), <基类n>(参数表n), 对象成员1(对象成员参数表1), 对象成员 2(对象成员参数表2), …, 对象成员n(对象成员参数表n) {...}
1.4.3 多继承 多继承下派生类的定义是按下面的格式: 1.4.3 多继承 多继承下派生类的定义是按下面的格式: class <派生类名> : [<继承方式1>] <基类名1>,[<继承方式2>] <基类名2>,... { [<派生类的成员>] }; 其中的继承方式还是前面的三种:public、private和protected。 例如: class A {...} class B class C:public A,private B
1.5.1 虚函数 [例Ex_VirtualFunc] 虚函数的使用 #include <iostream.h> 1.5.1 虚函数 [例Ex_VirtualFunc] 虚函数的使用 #include <iostream.h> class CShape { public: virtual float area() // 将area定义成虚函数 return 0.0; } }; class CTriangle:public CShape CTriangle(float h, float w) H=h; W=w; float area() return (float)(H * W * 0.5); private: float H, W; private: float H, W; }; class CCircle:public CShape { public: CCircle(float r) { R=r; } float area() return (float)(3.14159265 * R * R); } float R; void main() CShape *s[2]; s[0] = new CTriangle(3,4); cout<<s[0]->area()<<endl; s[1] = new CCircle(5); cout<<s[1]->area()<<endl;
习 题 定义一个描述学生基本情况的类,数据成员包括姓名,学C++英语和数学成绩,成员函数包括输出数据、置姓名和学号、置 习 题 定义一个描述学生基本情况的类,数据成员包括姓名,学C++英语和数学成绩,成员函数包括输出数据、置姓名和学号、置 三门课的成绩,求出总成绩和平均成绩。 2. 设有一个描述坐标点的CPoint类,其私有变量x和y代表一个点 的x、y坐标值。编写程序实现以下功能:利用构造函数传递参 数并设其默认参数值为60和75,利用成员函数display输出这一默认的值;利用公有成员函数setpoint将坐标值的修改为(80, 150),利用成员函数display输出修改后的坐标值。 3. 下面是一个类的测试程序,给出类的定义,构造一个完整的程序 执行程序时的输出为; 输出结果:200 – 60 = 140 主函数为: void main() { CTest c; c.init(200, 60); c.print(); }
习 题 4. 定义一个人员类CPerson,包括数据成员:姓名、编号、性 别和用于输入输出的成员函数。在此基础上派生出学生类CStu 习 题 4. 定义一个人员类CPerson,包括数据成员:姓名、编号、性 别和用于输入输出的成员函数。在此基础上派生出学生类CStu dent(增加成绩)和教师类CTeacher(增加教龄),并实现对学生和 教师信息的输入输出。 5. 把定义平面直角坐标系上的一个点的类CPoint作为基类,派 生出描述一条直线的类CLine,再派生出一个矩形类CRect。要 求成员函数能求出两点间的距离、矩形的周长和面积等。设计 一个测试程序,并构造完整的程序。 6. 定义一个字符串类CStrOne,包含一个存放字符串的数据成员, 能够通过构造函数初始化字符串,通过成员函数显示字符串的内 容。在此基础上派生出CStrTwo类,增加一个存放字符串的数据 成员,并能通过派生类的构造函数传递参数,初始化两个字符串, 通过成员函数进行两个字符串的合并以及输出。(字符串合并可使 用标准库函数strcat,需要包含头文件string.h)
习 题 定义一个抽象类CShape,包含纯虚函数 Area(用来计算面积)和SetData(用来重设 习 题 定义一个抽象类CShape,包含纯虚函数 Area(用来计算面积)和SetData(用来重设 形状大小)。然后派生出三角形CTriangle 类、矩形CRect类、圆CCircle类,分别求 其面积。最后定义一个CArea类,计算这 几个形状的面积之和,各形状的数据通过 CArea类构造函数或成员函数来设置。编 写 一个完整的程序。 8.上机练习本章的示例。