面向对象程序设计的三大特性:封装性、继承性和多态性。
类中成员的访问属性:public、private和protected。默认为private。
C++源程序文件类型:.cpp。
类是创建对象的模板,包含对象的状态描述和方法的定义。类的完整描述包括外部接口和内部算法。对象是正在创建的系统的一个实体。类是所有对象的共同行为和不同状态的集合体。对象是类的实例。
C++尽可能保持与C兼容。在此基础上又进行了许多改进。包括:
增加新运算符。如::,new,delete等。
改进类型系统,增加安全性。
如:类型转换多采用强制类型转换,函数说明必须有原型等。
引入了引用。引用作参数很方便。
允许函数重载,允许设置默认参数。
变量说明更加灵活,可以随用随定义。
C++是面向对象的,C是面向过程的。
面向对象程序设计将数据与对数据的操作放在一起,作为一个整体进行处理。
封装性是一种信息隐藏技术,使对象的设计者与使用着分开,使用者不知道对象行为实现的细节,只需要按设计者提供的命令去做即可。
C++特有的注释语句(相对于C语言)://。
将一般C程序改写为C++程序,需要做以下工作:
头文件:由#include <;改为
#include <iostream>
using namespace std;
输入语句:由scanf(“%d”,&s);等改为cin>>s;
输出语句:由printf(“%d”,s);等改为cout<<s;
const可用于修饰常量,函数参数,函数返回值,函数本身等。
尽可能避免使用C语言中的宏。因为宏都有副作用。对于不带参的宏,一般用const语句替换;带参的宏,采用inline函数替换。
const与指针(*)结合比较复杂,既可以修饰指针本身,也可以修饰指针指向的对象。可以这样记忆,比较const与*之间的位置关系。若const位于*之前,表示const修饰指针指向的对象;若const位于*之后,表示const修饰指针本身。
例:const char *cp=”JiangSu”;
char *const pc=”NanJing”;
判断下列语句的正确性并说明原因。
cp=”NanJing”;
cp[1]=’a’;
pc=”JiangSu”;
pc[1]=’i';
C++新增强制类型转换,类似于函数调用:类型 (表达式);
C++增大了全局变量的作用域。在局部变量与全局变量发生重名时,局部变量掩盖了全局变量。在局部变量作用范围内,为了使用全局变量,可以借助于域运算符(::)。
函数可以重载。重载函数的特点:函数名相同,参数不同(表现为:参数的类型、个数、顺序不同)。函数的常量性(常函数与非常函数)不同也可以重载。重载函数与带默认值的函数间可能产生二义性。在函数调用时,实参与形参类型不一致时,会进行自动类型转换,若转换不成功,则报错。仅有返回值的不同的两个函数不构成重载。
函数形参可以带默认值。在函数调用时,如果没有提供对应形参的实参值,则采用默认值作为实参。形参默认值一般在函数原型(声明)中给定,在定义时不再提供默认参数。默认参数值顺序从右向左赋予默认值。
例:函数重载的目的在于( )
A.实现共享 B.减少空间 C.提高速度 D.使用方便,提高可读性
例:判断下面原型的正确性:
void f(int i,int j,int k=10);
void f(int i=1,int j,int k=10);
函数定义在前,调用在后,调用前可以不必声明(定义同时就进行了声明)。
函数调用在前,定义在后,调用前必须声明。
函数参数传递的三种方式:
传值调用:实参向形参进行参数传递是单向值传递,并且分别占用独立的存储空间。
void swap(int x,int y){int t; t=x;x=y;y=t;}
传址调用:实参使用地址,形参采用指针,通过改变形参指针指向的实参值间接改变实参。
void swap(int *x,int *y){int t; t=*x;*x=*y;*y=t;}
引用调用:形参是实参的引用,改变形参即改变实参。
void swap(int &x,int &y){int t; t=x;x=y;y=t;}
认真体会上述三种方式。在C++中,尽量使用引用调用方式进行参数传递。
内联函数(inline,也称为内置函数,在线函数等)。函数调用会降低程序的执行效率,在调用函数时,需要保存状态和返回地址,然后转到子函数去执行。子函数执行完毕,又要取回先前保存的返回地址和现场状态继续执行,这些需要时空开销。对于一些功能简单、规模较小又使用频繁的函数,可以使用内联函数。内联函数在调用时不发生控制转移,而是在编译时将函数体嵌入调用语句处,节省参数传递、控制转移等开销,加快程序执行速度。(实质:以空间换时间。)内联函数比宏安全,因为它进行安全性检查。但inline关键字对编译器而言只起着建议作用。
函数可以递归调用。
函数的类型指的是返回值类型,默认为int。
函数返回值类型由定义时所指定的函数类型决定,而不是return语句的表达式类型决定。
例:要求函数实现一种简单的功能,且加快执行速度,选用( )
A.内联函数 B.重载函数 C.递归调用 D.嵌套调用
在C++中动态的内存分配与释放借助于new与delete实现,完成的功能与C语言中的malloc与free类似,但语法更简单,功能更强(在产生对象时,会自动调用构造函数;销毁对象时,自动调用析构函数)。因此,尽可能使用new与delete。
例:C语言中的char *p=(char *)malloc(10*sizeof(char));可以改写为:char *p=new char[10];
掌握:int *p=new int; int *p=new int(10); int *p=new int[10];之间的区别。
int *p=new int;表示:分配一个整型空间给p;
int *p=new int(10);表示:分配一个整型空间给p,并赋初值10;
int *p=new int[10];表示:分配连续10个整型空间(即数组空间)给p;
释放内存空间时,单个内存空间与数组空间不一样,不能混用。如对int *p=new int;释放时采用delete p;而对int *p=new int[10];释放时采用delete []p;
void指针表示通用指针或万能指针。可以把任意类型的指针或地址复制给它。反之则不允许,必须进行强制类型转换。void类型指针不能直接解引用,获取其所指向的对象。
异常处理中,throw用于抛出异常,try用于测试异常,catch用于捕捉异常。
引用是变量的别名。建立引用时,必须用另一个变量或对象名初始化。任何对引用的操作,实际是对目标的改动。引用与关联对象之间的类型必须一致。
例如:int a=3;则( )
A.int &b=&a; B.char &b=a; C.int &b=a; D.char &b=&a;
函数参数传递的三种方式:
传值调用:实参与形参分别占用不同的存储空间,实参向形参进行单向值传递。形参的改变不影响实参。
void swap(int x,int y) {int t; t=x;x=y;y=t;}
传址调用:实参使用地址,形参使用指针。通过改变形参所指向的对象来间接改变实参值。
void swap(int *x,int *y) {int t; t=*x;*x=*y;*y=t;}
引用调用:形参是实参(实参只能是变量)的引用。对形参的任意操作实际作用于实参。
void swap(int &x,int &y) {int t; t=x;x=y;y=t;}
认真体会上述三种方式。(读程序题6分)。
类是自定义数据类型,将数据(数据成员)与对数据的操作(成员函数)封装在一起。类中成员(包括:数据成员与成员函数)按访问权限划分为:public、protected和private。
Public成员是外界接口,可以任意访问;private成员只能在类内(由类的成员函数进行访问)访问;protected主要用于继承。
一般而言:数据成员设置为private,成员函数设置为public。
类中不允许对数据成员进行初始化。类使用在前,定义在后,则需要提前说明。类定义以;结束。类的成员函数可以在类内实现(默认为inline函数),也可以在类外实现(需要加类名::)。
例:试建立一个student类,包括姓名、年纪、成绩,设置成绩,打印等函数。学习类的建立方法。
类定义完成,就可以产生对象。(类和对象的关系类似于数据类型与变量的关系。)
对象要访问类中的成员(只有public成员可以访问,否则会产生编译错误)可以通过点.运算符。
使用类也可以定义指针,数组等。用法基本和C语言一致。
类中的成员函数都有一个隐含指针,称为this指针,指向当前主调的对象的地址。
在类中,有几个特殊的成员函数,起着十分重要的作用。(考试重点)
构造函数:在创建对象时,使用给定的值给对象初始化。
特点:函数名与类名相同。不能指定返回值类型(并非返回值为空)。
可以重载。一般为public。由系统自动调用。
参数可以带有默认值。
若没有定义任意构造函数,会由编译器产生一个缺省构造函数(无参数,函数体为空。)
构造函数中,有一种特殊的构造函数,称为拷贝(copy)构造函数,作用是用已知对象来创建同类对象。
copy构造函数特点:
与类同名。含有唯一一个参数,是某对象的引用。
每个类都必须有一个拷贝构造函数。
当类中数据成员含有指针时,一般需要自定义拷贝构造函数,否则产生浅拷贝。
析构函数:对象生命周期结束,清除对象所占用的系统资源。
与类名同名,在类名前加~。
没有任何参数,不能重载。(每个类最多有一个析构函数)不能指定返回值类型。
一般由系统自动调用。
若没有定义析构函数,会由编译器产生一个缺省析构函数(函数体为空。)
填空:( )对对象进行初始化。
拷贝构造函数的参数为( )。
当对象产生时,自动调用( );当对象生命周期结束时,自动调用( )。
判断:下面那个是拷贝构造函数。
MyClass a; MyClass b=a;
MyClass a,b; b=a;
例:class String
{
public:
String(const char *str=NULL); //构造函数
String(const String &other); //拷贝构造函数
~String(void); //析构函数
String& operator=(const String &other); //等号操作符重载
ShowString();
private:
char *m_data; //指针
};
String::~String()
{
delete [] m_data; //析构函数,释放地址空间
}
String::String(const char *str)
{
if (str==NULL)//当初始化串不存在的时候,为m_data申请一个空间存放'\0';
{
m_data=new char[1];
*m_data='\0';
}
else//当初始化串存在的时候,为m_data申请同样大小的空间存放该串;
{
int length=strlen(str);
m_data=new char[length+1];
strcpy(m_data,str);
}
}
String::String(const String &other)//拷贝构造函数,功能与构造函数类似。
{
int length=strlen);
m_data=new [length+1];
strcpy(m_data,o);
}
String& String::operator =(const String &other)
{
if (this==&other)//当地址相同时,直接返回;
return *this;
delete [] m_data;//当地址不相同时,删除原来申请的空间,重新开始构造;
int length= strlen );
m_data=new [length+1];
strcpy(m_data,o);
return *this;
}
String::ShowString()//由于m_data是私有成员,对象只能通过public成员函数来访问;
{
cout<<this->m_data<<endl;
}
main()
{
String AD;
char * p="ABCDE";
String B(p);
AD.ShowString();
AD=B;
AD.ShowString();
}
友元可以是一个函数,称为友元函数;可以是一个类,称为友元类。友元需要在类内说明(前面加关键字friend)。友元函数不是成员函数,但可以访问类内所有成员。它破坏了类的封装性与隐藏性,但提高了程序的效率。
static数据成员供类中所有对象共享,类中只存在一份拷贝。
在类中声明时需要加关键字static。
初始化需要在类外进行,且前面不能再出现static。
公有的static数据成员可以由类名(类名::)直接访问(即产生任何对象之前即可访问)。
也可以和非静态公有数据成员一样,由(对象名.)方式进行访问。
若静态数据成员非公有成员,需要访问时,只能借助于静态成员函数实现。
静态成员函数和成员函数相比,在返回值前增加关键字static。
静态成员函数主要作用就是访问静态数据成员,不能直接访问非静态数据成员或非静态成员函数。
在类外定义静态成员函数时,不能再出现static。
静态成员函数不含有this指针。
const可以用于修饰类中数据成员,成员函数的参数,返回值,函数本身(即常函数),以及类的对象。常数据成员只能借助于初始化表完成初始化。
常成员函数不能对类中任何成员进行修改。
常成员函数不能调用普通成员函数。而普通成员函数可以调用常成员函数。
常对象只能调用常成员函数。
特别注意:常函数的语法格式。
例:函数名f,含有一个整数引用参数,返回值为int,为了不改变参数,函数声明格式为:
( )。
函数名f,是类的常成员函数,无参数,无返回值,在类内声明格式为:
( )。
一个物体由多个物体组成,采用类的组合描述他们之间的关系。
在调用构造函数时,先调用成员对象的构造函数,再调用类本身的构造函数。析构函数相反。
若对象成员构造含有参数,参数传递通过初始化表完成。(具体过程看书C)。
继承可以分为单继承与多继承。
继承方式有3种:public继承、protected继承和private继承,默认为private继承。
继承提高了代码的可重用性。
基类成员的访问属性在派生类中会发生变化。
基类中的私有成员不能被派生类继承。
基类的公有和保护成员在派生类中取基类成员的访问方式及派生类继承方式中较小的一个。
(public>protected>private)。
需要注意,类的内部访问与类的外部访问的区别。
类的内部访问一般指的是类的成员函数可以访问那些成员,包括本类中的所有成员以及继承而来的成员。
类的外部访问一般指的是由类产生的对象可以访问那些成员,一般只能类的公有成员。
能够对类的内部访问和外部访问的成员及属性进行分析。见P160T3。(分析题6分)
例:类的保护成员经过私有继承后,相当于派生类的( )成员。
涉及继承时,构造函数调用顺序:先基类,接着成员对象,最后派生类;析构函数正好相反(P97,T3-1;P160,T4-1,2,3读程序题6分)。
在派生类中发生同名冲突时,一般借助(类名::)进行解决。
而为了消除多重继承中共同基类的多份拷贝,可以采用虚基类进行解决。
虚基类保证了共同基类的构造函数最多调用一次。
涉及虚基类时,构造函数调用顺序:先虚基类,再一般基类,接着成员对象,最后派生类。
例:设置虚基类的作用是(消除二义性)。
赋值兼容规则:需要使用基类的地方可以用公有派生类代替。
基类对象=派生类对象;
基类指针=派生类对象地址或指针;
基类引用=派生类对象;
多态性:一种行为对应着不同的实现。
可以分为:静态多态性(编译时多态性)和动态多态性(运行时多态性)。
静态多态性分为:函数重载和运算符重载。
运算符重载:友元函数重载和成员函数重载。
动态多态性实现的基础:虚函数、公有继承、指向基类的指针或引用(根据空的多少从前向后填写)。
运算符重载:根据运算符划分(选择题)
不能重载的运算符:. .* :: ?: sizeof(这些运算符本身就具有通用性)
只能采用成员函数重载的:= () [] ->
只能采用友元函数重载的:>> <<
既可以友元重载也可以成员重载的:算术运算符,关系运算符,逻辑运算符等。
运算符重载的语法格式及调用格式。(分成员函数重载和友元函数重载两种方式分别记忆)
函数在类的继承体系结构中有三种形式:
一般函数:派生类中继承了接口及实现,一般不要对其进行改写。
虚函数:派生类继承了接口及实现,但可以对实现进行改写。
纯虚函数:派生类只继承了接口,需要对其进行实现。
虚函数在声明时返回值前面需要加上关键字virtual。
派生类中对虚函数进行改写时,与基类中的虚函数原型要完全一致。(见课本例6.9)
虚函数是运行时动态确定的,而构造函数、内联函数及static成员函数是编译时确定的。
故虚函数不能使构造函数,构造函数也不能使虚函数,其他同理。
析构函数可以是虚函数。
含有纯虚函数的类称为抽象类。
纯虚函数一般没有函数体。
纯虚函数在派生类中必须重新定义,否则派生类仍然是抽象类。
抽象类不能实例化。
模板的主要作用提高代码的可重用性。实现方法:数据类型参数化。
格式举例:template <class T>
void swap(T a,T B){ T t; t=a,a=b;b=t;}
可分为:函数模板与类模板。
函数模板实例化得到模板函数;类模板实例化得到模板类。
模板函数可以重载。
在函数调用时,一般先匹配一般函数,再匹配函数模板。
匹配函数模板时,不会进行自动类型转换;需要程序员强制执行。
cin是标准输入流对象,cout是标准输出流对象。
练习题:P204 T1T2,P121T1 ,P48T4-2T4-6,P160T4-1,4-2,4-5,4-6
例:实现下面类中的函数。
class Point
{
double x,y;
public:
Point(double a=0,double b=0);//构造函数
double GetX();//获取点的横坐标
double GetY();
friend double GetLength(Point &A,Point &B);//计算点与点之间的距离
};