2019-01-07 c++复习
知识点:
· 在函数声明中也可以不写形参名,而只写形参的类型,如float add(float, float);这种函数声明称为函数原型(function prototype)。
· 纯虚函数声明: virtual 函数类型 函数名 (参数表列) = 0;
· 建立类模板的对象时,首先需要将类模板实例化,即想模板传递参数完成类模板的实例化,然后在定义该类的对象。模板函数只有在使用时候才会进行实例化。
· 类中定义的成员默认为 private 访问属性
· c++空类,成员函数有:默认构造函数、默认拷贝构造函数、默认析构函数、默认赋值运算符
· 访问器取名为getXX(),修改器取名为setXX()
· 常用友元函数重载输入输出运算符。“<<,>>”
概述
常量定义:只能读不能改;
eg:const float pi=3.14159;
设置输出的宽度:
eg:cout << setw(8) << 10 <<endl;
输出结果是:_ _ _ _ _ _ 1 0
字符填充输出宽度:
eg: cout<<setfill(‘*’)<<setw(3)<<25<<endl;
输出结果是:*25
控制左右对齐:
eg:cout<<setiosflags(ios::right)<<setw(5)<<1<<setw(5)<<2<<endl;
输出结果是:_ _ _ _ 1_ _ _ _2
算术运算的类型转换规则:朝表达数据的精确度更高的方向转换
char-》short-》int-》long int-》float-》double-》long double
函数与数组
函数调用是采用栈结构。栈:先进先出。
函数不会修改实参,除非传入的是地址,实参的值才会改变。
引用
声明一个引用必须在定义的时候就初始化。
int a;
int &b=a;
引用的值一旦变化,被引用的值也会发生变化。引用被重新赋值后,引用地址仍然绑定在目标变量地址上。
声明了引用之后,在使用的时候不用带&,直接用起的名字如b就行。用引用时如果加了&如&b就是指取a的地址。
常引用: const float& r ,好处是既不会更改所引用的对象,保证数据的安全,又没有传值所需的实参向形参拷贝过程,这将会改善程序的运行效率。
全局变量
全局变量通常定义在main()函数之前,一旦被定义可被后面的函数访问。ps:尽管全局变量可以定义在任何函数之外,但是定义点之前的函数是不可知的。
全局变量有缺省初值:0;
局部变量
存放在栈区中,函数被调用时,局部变量在栈区被分配存储空间,函数调用结束时,局部变量随之消失,其值不能保存。生命期是在函数调用期间。局部变量在c++中没有初值。
静态变量
静态局部变量
也是在函数内部定义的变量,但如果没初始化,有默认值0.编译器对静态局部变量仅初始化一次,重复调用保留原值,不再进行初始化。ps:静态局部变量多次函数调用仅初始化1次。
静态全局变量
不必担心其它源文件使用相同变量名,彼此相互独立。在某源文件中定义的静态全局变量不能被其他源文件使用或修改。 只能在本文件中使用!具有内部链接的静态;不允许在其他文件里调用;
重载函数
函数的形参不同。
如果形参相同,返回类型不同,编译器无法区分调用哪个重载函数。
string.h
字符串连接函数strcat
调用形式:
strcat(字符数组1,字符数组2);
功能:将字符数组2连接于字符数组1的后面.
字符串拷贝函数strcpy
调用形式:
strcpy(字符数组1,字符数组2);
功能:将数组2 拷贝到 数组1中.
字符比较函数strcmp
调用形式:
strcmp(字符串1,字符串2);
功能:将两字符串的字符从左到右逐个进行比较.返回值为比较结果.
字符串1=字符串2 返回值为0
字符串1>字符串2 返回值为正数
字符串1<字符串2 返回值为负数
测试字符串长度函数strlen
调用形式:
strlen(字符数组);
功能: 测试字符数组中的字符串长度.
指针
指针前,必须给已定义指针变量赋以指向目标变量的地址值。“&”操作符可获取变量的地址。
int num, *pn;
pn=#
*pn=100; //等效于num=100;
指针运算
int x;
int *ptr=&x;
&(*ptr) 表示指针ptr
*(&x) 表示变量x
堆内存分配
申请动态内存分配函数和释放分配内存函数,其原型为:
void *malloc(size_t size);
void free(void *);
array=(int *)malloc(arraysize*sizeof(int))
free(array);
new和delete两个专用操作符,具有类似于malloc和free函数的功能。
new <类型说明符>[<算术表达式>];
delete的使用格式:delete[ ]<指针名>;
float *p;
p=new float[10];
delete[ ]p;
eg:if((array=new int[arraysize])==NULL)
……
delete[ ]array;
const指针
指向常量的指针
不可以修改指向的常量的值
const int a=80;
const int b=15;
const int *pi=&a; //定义pi 为指向常量的指针
*pi=93 //错误,不能修改指向的常量值
pi=&b//正确,指向另一个常量
指针常量
指针常量的地址不能被修改,内容可以修改
char *const pc=“asdf”;
pc=“dfgh”; //错误,不能修改指针常量中的地址
*pc=‘b’; // 正确,内容被修改为“bsdf”
指针与函数
例子:
声明:void swap(int *,int *);
定义:void swap(int *x,int y)
{
int temp=x;
x=y;
*y=temp;
}
调用:swap(&a,&b); //传址调用
结构体
结构体与指针
假设person是一个结构体。
struct person
{
char name[10];
int age;
float wage;
}
person *p; //定义指向结构类型的指针
person pr1; //定义结构类型变量
若p=&pr1;则p 是指向结构变量pr1 的结构指针,为访问成员,可写成:
(p).name 等效于 p->name;
(p).id 等效于 p->id;
(*p).salary 等效于 p->salary.
类
关键字public、private、protected
公有(public)成员提供了类的接口功能,不仅可以被成员函数访问,而且可以在程序中被访问;
私有(private)成员是被类隐藏的数据,只有该类的成员或友元函数才可以访问,通常将数据成员定义为私有成员;
保护(protected)成员具有公有成员或私有成员的特性。
类中的内联函数
在类体外定义成员函数时须按下述格式:
<函数类型> <类名>::<成员函数名>(<参数表>)
{
<函数体>
}
构造函数,析构函数
构造函数
每当创建一个对象时,可以自动调用类的构造函数给对象进行初始化。
构造函数可以在类体内定义也可以在类体外定义。体外加(类名::)
重点注意的调用顺序:若类的数据成员是另一个类的对象,则在调用构造函数创建对象时,对作为数据成员的对象先要自动调用其自身的构造函数。
析构函数
特点:
无返回类型; 无参数; 不能随意调用; 不能重载。
析构函数与构造函数的功能相对应,所以析构函数名是构造函数名前加一个逻辑反运算符“~”;
拷贝构造函数
指一种特殊的析构函数。
函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
每调用一次构造函数,对应一次析构函数。
先执行构造函数,然后拷贝构造函数,最后析构。
classname (const classname &obj) {
// 构造函数的主体
}
静态成员和友元
对静态数据成员的值的更新,即是对所有对象的该静态数据成员值的更新。
静态数据成员要在类体外进行初始化。
静态数据成员的生命期是全局的,编译器只对它进行一次初始化,将始终保持其值,直到下次改变为止。
静态成员函数:
M::fun(P); //类名调用
q.fun(q); //对象调用
友元函数
可以不通过调用成员函数就可以直接访问类的私有数据,以提高程序运行效率。
友元函数是非成员函数,在调用上与普通函数相同
继承和派生类
继承
继承是指把已有的类作为基类来定义新的类。新类继承了其基类的属性和操作,还可以具有其基类不具备的自己特有的属性和操作。
1、公有继承方式(public)
基类中的每个成员在派生类中保持同样的访问权限;
2、私有继承方式(private)
基类中的每个成员在派生类中都是private成员,而且它们不能再被派生的子类所访问;
3、保护继承方式(protected)
基类中的public成员和protected成员在派生类中都是protected成员,private成员在派生类中仍为private成员。
权限:1、不管是什么继承方式,派生类的成员函数和友元函数都可以访问基类中的公有成员和保护成员,但不能访问私有成员; 2、在公有继承时,派生类的对象只能访问公有成员,在保护继承和私有继承时,派生类的对象不能访问基类中任何成员。
构造函数的调用顺序:基类构造函数;子对象构造函数;派生类构造函数。
二义性
C1.f();//f()在A,B两个类里面都有则出现二义性
C1.A::f(); 或者C1.B::f();//调用方式改变一下。
虚基类
有时候应用虚基类来解决二义性问题
虚基类说明格式如下: virtual <继承方式><基类名>
目的:使公共基类在其派生对象中只产生一个基类子对象。
多态性
子类型
指向基类指针不能访问派生类定义的成员
静态联编
基类的指针P,P 指针不能直接访问派生类中的成员函数,所以必须显式地用派生类建立的对象调用派生类函数。
动态联编
通过使用虚函数来实现。
某个类中的一个成员函数被说明为虚函数,该成员函数可能在派生类中存在着不同的实现版本。
在派生类中的虚函数前的virtual关键字可以省略。
函数模板
定义:任意类型T 为参数
template <参数化类型名表>
<返回类型> <函数名> (<参数表>)
{
//<函数体>
}
函数交换模板:
Template <class T >
void swap(T& a, T& b)
{
T temp=a;
a=b;
b=temp;
}
求最小值的类模板:
template <class T>
T min(T a, T b)
{
return a<b?a:b;
}
使用冒泡排序法:
template <class stype> //Function template definition
void bubble(stype * item, int count)
{
int i,j;
stype t;
for(i=1; i<count; i++)
for(j=count-1; j>=i; j--)
{
if(item[j-1]>item[j])
{
t=item[j-1];
item[j-1]=item[j];
item[j]=t;
}
}
}
int main(){
int nums[]={9,4,2,6,8,5,1};
bubble(nums,7); //Calling template function调用
}
类函数模板:
template <模板参数表>
class <类名>
{
//类体说明
};
template <typename T>
class Complex{
public:
//构造函数
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
//运算符重载
Complex<T> operator+(Complex &c)
{
Complex<T> tmp(this->a+c.a, this->b+c.b);
return tmp;
}
private:
T a;
T b;
}
int main()
{
//对象的定义,必须声明模板类型,因为要分配内容
Complex<int> a(10,20);
return 0;
}
运算符重载
它是一种特殊的成员函数,其语法形式为:
type X::operator@(参数表)
{
//相对于该类而定义的操作(运算符重载函数体)
}
一元运算符
隐式调用:@objx 或者objx@
显示调用:objx.operator@( )
二元运算符
隐式调用:objx1@objx2
显示调用:objx1.operator@(objx2)
例子:
void Counter::operator++( )
{
if(value<65535) value++; //“++”是重载符吗?
}
void Counter::operator--( )
{
if(value>0) value--;
}
文件
描述标准输入输出操作及文件操作的类fstreambase 类 ——公共基类,文件操作不使用;
ifstream 派生类——对文件进行提取操作,即读操作;
ofstream 派生类——对文件进行插入操作,即写操作;
fstream 派生类——对文件进行提取和插入操作,即读/写操作;
filebuf 类——对上述的缓冲支持。
iostream.h 下面的ostream
I/O 流中提供的成员函数put(),可以输出一个字符。ostream& cout.put(char c);
或 ostream& cout.put(const char c);
例子:cout.put(‘B’).put(‘E’).put(‘I’).put(‘J’).put(‘I’).put(‘N’).
put(‘G’).put(‘\n’);
用write输出一个字符串
cout.write(const char *str, int n);
其中,str 是一个字符指针或字符数组名,也可以是字符串常量;n指定输出显示字符串中字符的个数。当参数为strlen(str)时,则表示输出显示整个字符串。
用write把指定长度序列插入到输出流
ssize_twrite(int handle, void *buf, int nbyte);
handle 是[文件描述符]
buf是指定的缓冲区,即[指针],指向一段内存单元;
nbyte是要写入文件指定的字节数;返回值:写入文档的字节数(成功);-1(出错)
cin.get() 1: cin.get(字符变量名)可以用来接收字符 2:cin.get(字符数组名,接收字符数目)用来接收一行字符串,可以接收空格
cin.getline(char *buf, int n, char deline=‘\n’); // 接受一个字符串,可以接收空格并输出,buf 存放从输入流中提取的字符序列;n 限定从输入流读取的字符个数不得超过n-1个,deline 用以限定一行字符的结束符号,默认参数为‘\n’。
getline() // 接受一个字符串,可以接收空格并输出,需包含“#include”
gets() // 接受一个字符串,可以接收空格并输出,需包含“#include”
用read() 读取若干字符:cin.read(char *buf, int size);//可指定数目
文件的打开或关闭
fstream <对象名>;
<对象名>.open(“<文件名>”,<方式>);
或简写为:
fstream <对象名>(“<文件名>”,<方式>)
例子:
fstream outfile;
outfile.open(“file.txt”,ios::out);
//简写
fstream outfile(“file.txt”,ios::out);
其中,outfile 是类frstream 的对象,ios::out表示打开文件进行写操作。
常用的文件访问方式
待关闭的流对象调用关闭成员函数close()。
outfile.write(指针,大小)将指针所指区域指定大小的数据写入文件
也就是说,把stud中的第i个记录写入文件
格式为:
<流对象>.close();
例如:关闭前例中已打开的流对象
outfile.close();
随机访问数据文件
istream类或ostream类image.png
tellp()和tellg()函数用以返回当前读写指针距文件头的字节数。返回值是long int 类型量。
image.png
例子:该指针移动到当前位置前100 个字符处(向文件头方向移动);
input.seekg(50, ios::beg);
fileb.seekp(sizeof(student)*4); //移指针到第五个元素起始位置
seekg()是设置读位置,seekp是设置写位置