C++基础语言

标识符:即变量名,函数名,宏名等

存储类型:auto(默认不写) extern static

A类内函数:B类名 & 函数名 {} 意思是返回值是B类的引用

&& 右值引用 :即将消亡的值

& 左值引用

被继承已有类:基类(父类)

派生出的新类称为派生类(子类)

对象成员:A类对象作为B类成员时,A类对象称为对象成员

最远派生类:建立对象所指定的类

&函数名:返回值是地址值,可以节约空间,而不必再创建一个临时对象赋值

群体:由多个数据元素组成的集合体

线性群体:按位置排列有序的

非线性群体:不按位置顺序来标识元素

U:前置单目运算符

引用

和指针一致,传地址到子函数中,值可以直接被修改

带默认行参的函数

clock(int x,int y=2)

必须要从左往右赋值,因为参数入栈顺序是从右往左

初始化列表和缺省参数值

初始化列表:在函数之后冒号

​ clock(int x,int y): hour(x),miniute(y){}

缺省参数值:默认值

构造函数

没有返回值

析构函数

没有返回值,如果未定义,那么就只有系统默认的空函数体的析构函数,是在对象生存期快要结束的时刻被自动调用,对象的内存空间被释放

块作用域、

例如子函数中的形参,到大括号结束

类作用域

引用这个类的对象

例如在主函数中声明了myclock,那么这个主函数也是myclock的类作用域,或者某一子函数中声明引用

重载函数

函数名相同,但是形参的数量和数据类型是有一不同,且不以返回值类型进行区分。

友元

class A { public: friend class B} B就可以直接访问A的private 数据

单向,非继承

在类中对数据直接初始化

c++11中是可以的,但是初始化的值会被构造函数初始化列表的值所覆盖

常对象

const clock myclock

前面或者后面有const 只能调用对象里面的常成员函数或者静态函数(本质上调用静态函数时候只是借用了常对象实质上是调用了类)

常成员函数

void showtime() const //const只能放后面如果放前面意味着返回值是常量

常成员函数是只能读取同类数据成员的值而不能修改他

const可以用于重载,如果非const对象调用该函数时。调用没有const的重载函数

静态成员函数

只属于类,其余非静态成员函数(包括常成员函数)是属于对象的。

静态数据

static int count;

const clock ::count=0;

静态数据成员只能再类定义之外加以赋值

const相关

普通数据:如果是int 或者 const int 的话就可以互相赋值但是const int 必须初始化

指针:指针类型必须要和所指的对象类型相同 例外一:const int* 指向常量的指针可以指向非常量的指针int (指针类型的常量:int* const )注意指向字符串的指针是不能通过指针来修改字符串的等价于 char *p= … const char *p=…..

引用:const int&可以引用一个非const的对象:const int & = int ,同样const int x=double (只是在c++中本质上创造一个const int temp= double ,再让x=temp,所以x实质上是引用一个临时量temp,所以x只允许是const类型,才可以跨类型“引用”)

所有的指向常量的指针或者是引用其实都是无法通过引用或者这个指针来修改他原本的值。const int &或者const int*

静态的全局变量不能被其他文件共享,但是全局变量可以

静态常数据

static const int b

同静态数据只能再类定义之外加以赋值,但是如果是具有int和enum类型就可以直接写,若在类中已经定义了初值,那么不能在类定义外再赋初值

常数据成员

const int a或者int const a

不能在类中直接赋值,只能通过构造函数并且必须在所有的构造函数中进行初始化

常引用

类里面{int sum(const clock &clock1,const clock &clock2)}

const clock myclock(1,2),yourclock(3,4);

int sum(myclock,yourclock)

可以让myclock和yourclok避免被修改。

多文件结构和编译预处理

c++一般组织结构

三个文件:

.h)类的定义 不需要空间的声明以及内联函数的的定义,外部变量和外部函数的引用性声明,不要给变量定义性声明(赋值)

.cpp)类的实现,类里面的函数的具体实现

.cpp)主函数只能写一个

两个cpp文件都需要包含include文件,在被编译成obj文件后3个文件再连接成exe文件

文件之间共享数据问题

extern +全局变量:文件之间共享数据

static +全局变量:取消文件之间的共享

外部变量(本文件的全局变量)

用于声明外部变量

extern int x 一般只用于声明变量,如果未赋值那么就是引用性声明,赋值就是定义性声明,但是变量只能有一处的定义性声明。如果在基本无法在函数内部对一个变量定义声明。引用式声明的作用域与原变量一致。

外部函数

在所有类外的可以加extern

extern

int和 extern int 的区别

错误:例如 h文件中 int x 这时候是不能赋值的,然而 这时如果在cpp中int x或者int x=的话就会重定义报错

解决:h文件中extern int 不可赋值 ,再到cpp文件中进行赋值即可,并且 这个只占一个内存空间

注意编译过程是先各个文件编译再进行链接的这个extern只在链接时候起作用,在编译文件时候就只是告诉编译器这个是是一个来自其他文件的变量,虽然找不到他但是不会报错

匿名的命名空间

不希望被其他编译单元(一个cpp源文件)引用的函数和变量都在这

namespace { int n;

宏定义

#ifndef< if not define>//测试是否被宏定义过,定义过返回假

#define 1 //正文

#else 2 //如果定义过则编译2

#endif //终止

数组

二维数组在内存中是按行存放的

对象数组

clock a[n],这里每一个a 用于存放类中的所有私有数据,默认是调用构造函数进行对私有数据进行初始化,如果不想初始化赋值那么就需要定义一个默认的构造函数(不可以少)

例如 clock public:clock(int x=1 ,int y=2):x(x),y(y){}

pirvate:int x,int y. int main(){ clock a[2]} ,那么a[0]={x= 1,y=2} a[1] ={x= 1,y=2}

指针(声明,赋值,引用)

int *p=NULL == int *p=0;

指向常量的指针

const int *p=&a;p可以改,但是不可以通过 *p来修改a

指针类型的常量

int const *p= &a ,p不可

引用(并不是一个对象)

int &refVal=ival refVal是ival的另外的名字(引用必须被初始化)相当起了另外一个名字没有地址,并且类型需要一致 ival也要int,并且只能绑定到对象上,不能int &refVal=1;,注意指针无法指向引用

不合法指针

指向错误内存地址或野指针,即必须保证操作指针时,指针所指的对象是有效的,指针不为空。也需要类型一致

指针是否有效

p==NULL,空指针就无效

其他指针操作

其他指针操作可以用作bool类型进行判断详情见c++primer第五版 p50,例如int*p=0;if( p)

如果==,比较的是地址值,而不是变量值。

指针有关的bool

int a=5;int *p= a;

if(p):p判断是否为空 if(*p):判断a是否为0;

函数指针

存储类型 类型 (*函数指针名)(形参的数据类型)

void (* function)(float)

存储类型:auto(默认不写) extern static

指向函数

void showtime(float a)

funciton=(&)showtime;//指向函数,&可写可不写+++++++++++++++++++++++++++++++++++++++++++++++

function(5.1)//调用函数

*

对象指针

类名 * 指针名

class Clock ; Clock myclock ; Clock *point;point=&myclock;

运用

point->showtime(); or (*point).showtime;

注意

1.每个对象初始化之后所占据的空间只是数据成员的空间,并没有函数副本,并且类在未初始化之前并没有分配空间地址,只有实例化之后才分配

2.必须初始化指向已经声明的对象,只可以访问类的公共成员

this指针

默认中存在,隐含在每一个类的非静态成员函数中的特殊指针,指向所操作数的对象

例如 在构造函数中 hour=x; 实际上 this->hour=x;

作用

指出成员函数当前所操作的数据所属的对象,对常成员函数来说,这个this指针就是指向常量的指针(常指针类型)

当局部和外部作用域中有同名的标志符的时候,若想让外部的标志符起作用的时候就可以用this

指向类的非静态成员的指针(只能是公共成员)

指向公共函数:(括号)

​ 声明:类型说明符 (类名::*类成员指针名)(参数表)void (Clock:: *time)();

​ 赋值:指针名=&类名::函数成员名 time=&Clock::showtime

​ 访问:对象名.*类成员指针名 或者对象指针名-> *类成员指针名

​ (myclock. *time)();

指向公共数据:

​ 声明:类型说明符 类名::*指针名 例punlic int hour; int Clock :: *hours;

​ 赋值:指针名=&类名::数据成员名 例 hours=&Clock::hours

​ 访问:对象名.*类成员指针名 或者对象指针名-> *类成员指针名 myclock.hours

指向静态的类成员的指针

指针无需特别定义,与一般指针是一致的

动态申请内存操作符new

声明:指针=new 数据类型名 T(初始化参数列表)返回指针

​ 例如:int *point; point=new int()括号可以不写就不初始化,如果括号内没有数据就是默认初始化为0

注意:如果new的是类的对象,就是调用构造函数,如果调用的是相同的默认的构造函数,还会递归地给基本数据类型和和指针类型用0赋初值,用new申请的空间必须由delete进行释放,否则会内存泄漏,而且只能进行一次

指向对象的指针时候:Clock *point=new Clock; delete point;

使用new创建对象数组或一般数组时,不能为该数组指定初始值,其初始值为默认值。

释放内存空间操作delete运算符

声明: delete 指针名;

注意:指针只能是由new操作所返回地,如果删除的是对象那么调用析构函数,但是指针本身地址还存在,指针不会被删除

动态创建数组类型

声明 :new 数据类型名[长度] 例 int *point=new int[10];同样可以申请多维

可以在[]之后加(),但是不能写数据进去。

加():与上面new对象一致,给每个数据赋值0

不加():不对每个数据进行初始化,不赋值

删除: delete[] 指针名 ,如果不写[]那么就只释放首地址

多维:int (*point)[8] [9]=new int[7] [8] [9]可以看成7个[8] [9]

vector

vector<类型>名称 +初始化

这个类型不能是引用

浅复制 只是针对类数据成员是动态数组类指针的时候

就是直接调用默认的拷贝函数,但是这时候是生成2个指向同一内存地址的指针,所以delete时候会出错

Arrypoint yourpoint(mypoint)

深复制 只是针对类数据成员是动态数组类指针的时候

在类写一个拷贝函数,就是再创建一个内存空间

Arryofpoint::Arryofpoint(const Arryofpoint & v){size=v.size ,for(){points【i】=v.points[i];}}一个个 复制过去

移动构造

直接将原对象的内存转移给新对象,而复制构造是再生成一个新空间而将旧空间delete

构造函数参数表(Clock &&n):points(n.points){ n.points=nullptr}

就是多了将旧指针指向空

string类对象 其实就是string数据类型(引用类型)

初始化必须包含string 文件

string()//建立一个 长度为0的串

string(const char *s)用s所指的字符串常量初始化string

string=“ ” 同样可以进行bool 加减 运算 > < !=

getline string文件

getline(cin,s1,“结束标志符”)

继承与多态

多态

多态行为基础:基类声明虚函数,派生类声明同名函数覆盖这个虚函数 需要函数签名(函数名 参数列表 const)完全一致

继承方式

默认是私有继承

共有继承:

​ 继承的访问权限:对基类的private 不可直接访问,这时基类的private继承下来已经发生变化(没有名称的类型)

​ 访问权限:派生类的成员函数可以直接访问基类的public和protected,如果要访问基类的私有成员需要借助基类的共有函数成员,protected成员在类中本来是与private相同,当是在派生类中就变成了public,private同样不能直接访问

私有继承:

​ 基类的全在私有成员中,要访问基类的私有成员,就得从派生类的函数调用基类的函数(这个基类函数是访问基类自己的私有成员)

保护继承:

在派生类中与public一致,在类外时候通过派生类对象不能访问基类任何成员(公有成员也无法直接访问)

多继承

class 派生类名:继承方式 基类,继承方式 基类,继承方式 基类{ 成员 }

派生类的构造函数

派生类名::派生类名(形参表):基类1(参数),基类2(参数)本类成员初始化列表{//其他初始化} 执行顺序按照继承的顺序,而不是按照构造函数上一行所写的数据

派生类的复制构造函数

如果没有调用默认,先调用基类的复制构造函数再进行对新成员进行初始化

C::C(const C &C1):B(c1){ }

默认的构造函数是直接将值复制完毕的,如果自己写的话不能是空定义

派生类的析构函数

先调用派生类的析构后调用基类的,与构造函数顺序相反,无需显式调用

访问基类被隐藏的成员(与派生类同名的函数)作用域分辨符::

只有在相同的作用域才可以函数重载,所以当派生类中声明了与基类同名的即使参数列表不同,从基类继承的同名函数也会被隐藏,这时要访问就得要用作用域分辨符

可以Clocks.Clock::showtime 类名限定

如果想要将基类的同名函数重载,那么就得用子类中using A:: showtime(int x)

例如Class Clocks: public Clock{public: using Clock::showtime(int x); void showtime(int x,int y);}

二义性问题

2个基类中存在相同的函数成员,用类名限定,或者在派生类中添加同名函数再用类名限定

虚基类

只有最远派生类调用,解决派生类从多个基类派生,而多个基类又存在同一基类,这时明显这个成员意义是单一的

必须要在一级继承的时候就添加 virtual关键词 例如2级继承爷父儿时候,必须要在这几个父继承时候添加class Clocks:virtual public Clock;

虚基类的构造函数

必须在子类的构造函数初始化中添加爷的构造函数Clock(int x):Grf(x)

调用构造函数顺序,如果有虚基类就先调用虚基类,然后按照类声明顺序

多态的实现

静态绑定:绑定在编译连接阶段完成:重载,强制,和参数多态(模板类)

动态绑定:绑定在程序运行阶段完成:包含多态(大多虚函数实现)

运算符重载为成员函数 <代码2>

运算符重载的实质是函数重载,只针对同一类的对象

oprd1 和oprd2 都是A类 ,双目运算符+ 如果要实现oprd1+oprd2 则应该在A类中添加运算符重载 oprd1.operator +(oprd2) 参数个数等于原操作数个数-1

单目运算符 前置时候oprd.opeator ++()里面为空 后置oprd.opeator ++(int )这个int 没有用只是用来区分前置和后置

代码:在public中A operator+(const A& c)const;声明

类外A A::operator+(const A& c)const {return A(real + c.real, image + c.image);}创建一个空对象并且返回A类的值(A类型可以返回是* this指针)

运算符重载为非成员函数 <代码3>

当是无法被修改的类的2个对象进行运算时候,例如对cout进行输出修改不仅仅是输出int float型,变成可以输出A类的数据时候就需要把重载运算符为非成员函数,参数个数等于操作数个数

双目代码: 最好在类中设置为friend A operator +(const A& c1, const A& c2) 比较好访问数据,类外A operator +(const A& c1, const A& c2) 定义

单目代码:前置operator U (oprd)后置 operator U (oprd ,int)必须多一个没有用的int形参

虚函数

本质上是原本函数定义形参时候就需要确定对象,但是虚函数告诉他等最后再确定对象,覆盖掉基类的虚同名函数

虚函数应该属于对象的,不内联,实现动态绑定

目的是爷父儿中存在同名的函数,只写一个形参是三者之一的函数来访问这个同名函数需要解决的问题<代码4>可以只在最老的基类中‘添加virtual,派生类中就不显式地使用virtual 声明虚函数,系统判断是否与基类虚函数名称,参数,返回值来判断,习惯都加virtual增加可读,虚函数会隐藏基类地所有同名重载函数只能通过类名访问

虚析构函数

与代码4基本一致,父子关系,想只通过一个带父指针参数地函数来删除一个用new出来地B *b =new C()的指针b 函数void fun(B *b)delete b ;这时只调用B类的析构函数同样得在析构函数加virtual使得BC2个的析构函数都可以调用

虚表
Snipaste_2020-04-09_17-31-09
pictureSnipaste_2020-04-09_17-31-09.png

vptr指针指向虚表,每个类中都有虚表

抽象类<代码5>

带有纯虚函数(暂时无法实现的函数)的类就叫抽象类,抽象类只做基类不能有自己的对象,目的是保证让派生类(自己实现具体功能)具有要求的行为,但是类的指针是可以作为形参的

纯虚函数:virtual 类型 函数名(参数表)=0;

c++11 override和final

override:当想通过派生类的函数去覆盖基类的虚函数,却因为函数签名不太一致例如少了const,而导致无法覆盖,这种错误往往不好找到,就在派生类需要覆盖的虚函数后面加override声明

例如 void show()override

final:

不允许被继承,成员被覆盖

Snipaste_2020-04-09_17-51-35
pictureSnipaste_2020-04-09_17-51-35.png

模板

函数模板

template < class或typename 名T>

函数(T 形参名) : void f(T x);

函数上面必须有template< >

调用f(y)如果y为int就将T变成int类型 ,double同

类模板 例题借鉴<代码6>

基本与函数模板一致,使用时候:类名<类型> 对象例如:Clock< int> myclock,yourclock;

大点的

动态数组类 代码编号1

在类的构造函数中point=new Point[size]完成对这个数组类的创建

未解决

静态数据常数据 静态常数据 是否类内可以赋值,以及赋值关系

#define 放在双目计算的中间

闰年计算 清华c++p17

代码6