C++学习笔记2019

一:数组
Float f[10];不允许动态给数组定义长度 从下标0开始进行计数,取出数组的元素用a[]
二维数组的存储结构 第一行存完再存第二行……赋初值的时候可以将一行的用一个花括号框起来
当然前面需要先定义a的类型 Cin>>a 可以直接给a进行赋值 不需要像c语言那样用scanf(%d,&a);
字符数组的初始化 char ch1[4]=“abc”(用字符串赋值的话数组大小必须要比字符数多一 最后一位保存\0) 也可以char ch2[5]={‘a’,‘v’,‘d’,‘e’,‘t’};
输出字符串的时候可以直接用字符串数组的名字 比如cout<<ch2<<endl;
Strcpy_s(str1,str2) 把str2->str1; strcat(s1,s2) 把s2添加到s1尾部
Strcmp(s1,s2) s1>s2=1 s1=s2=0 s1<s2<0
Strlen(s) 返回s的字符长度

指针 int*p=&a p存的是地址 *是去内容运算 &是取地址运算
数组指针 本身是一个指针,指向一个数组 int *p=a(int a[10]? 或者int * p=&a[0]
int (p)[5];
行指针:int (p)[4] int a[3][4] p=a
(p+i)+j)表示a[i][j]的值
列指针 int *p int a[2][4] p=&a[0] (p+(i4+j)表示a[i][j];

指针数组 本身是一个数组,内部存的全部是指针
Int *p[5] 每一个都表示一个int型的指针

New delete
New 一个类型 eg:folat *p=new float(9.999); 或者a=new int[]
Delete 指针变量 eg:delete p;
如果需要动态生成一个数组 eg:int size=100; char *pt =new char [size]%%这个情况可以预定义大小 delete的时候就需要 delete []pt 在动态生成的名称前面加上一个[]
结构
格式:struct date%结构名称{
数据类型 成员名;
数据类型 成员名;
};
声明一个结构的时候可以用 date mondy={1,2}; 并对其初始化
当访问结构体里面的东西的时候,可以用 date.成员名
当然也可以定义结构数组 date day[100];
有了数组也就有了指针 date day ;date mondy; day=&mondy 用的时候就是(day).成员名 访问变量的 . 可以换成->

函数
第一种 实参数据值传递
eg:void swap(int x,int y) { Int temp; Temp=x;x=y;y=temp; }
这种情况不会改变 传进去的x,y的值,只是内部的参变量的值发生了改变
还可以进行默认参数 定义的时候 void swap(int x=2,int y=8)

第二种 是惨递质的传递
void swap(int *x,int *y) { Int temp; Temp=*x;*x=*y;*y=temp; }
这样x y的值也会随着发生着改变

内联函数
用来防止因为一直调用函数而引起的额外的开销 对于一些需要经常使用到的函数,可以作为内联函数 对于普通的函数,是运行到函数,然后跳转函数语句,而对于内敛函数是将该段代码直接加到主调函数里面,不需要执行完再返回到调用点,提高了效率,但是这是以增加程序代码作为代价的
格式:inline 类型 函数名(参数表)

inline int count1(int a)
{
	a = a + 2;
	return a;
}

函数的重载
就是说用一个函数名可以因为参数个数以及类型的不同而调用不同的函数版本

 int count1(int a)
{
	a = a + 2;
	return a;
}
 double count1(double a)
{
	a = a + 2;
	return a;
}

类的声明和对象的定义
类和结构体的区别:结构体只有数据,没有操作 对于类来说, 可以有操作
在定义方面与结构体比较相似

class student {
	int num;
	int olds;
	void display()
	{
		cout << num << endl;

	
}
}

一般来说,都是将数据进行隐藏,而对成员函数作为外界的接口
因此就更改函数

class student {
	Private:
		int num;
		int olds;
	Public:
		void display()
		{
			cout << num << endl;

		
}
}

如果在最开始的时候没有定义是private还是public 那么就默认为private
定义格式 class student{
Private 只允许本类的成员函数来进行访问
Public 是与外界的接口
Protected 用于类的继承和派生(之后会讲到),外界不可以访问,但是允许类内成员函数以及派生类成员函数进行访问
}

对成员函数的引用
记住,类的外界只能够访问public部分,无论是private还是protected,都不能够访问
访问形式和结构体一样 student L; L.display() 或者L->DISPLAY()

类的成员函数
类的成员函数可以在类的定义体内进行定义,也可以在类外进行定义
例如

class student {
	Private:
		int num;
		int olds;
	Public:
		void display()
}; 
Void  student :: display()
{
	cout << num << endl;

}  

在类外进行成员函数的定义的时候,必须要加上::
对于程序员,最好在类内给出成员函数的定义,在类外给出成员函数的具体实现
这样可以将类的定义和实现相互分离,提高程序的可读性

成员函数的内联函数形式

class student {
	Private:
		int num;
		int olds;
	Public:
		void display()
		{
			cout << num << endl;

		
}
}

如果直接在类内进行定义,那么就是将其定义为内联函数

class student {
	Private:
		int num;
		int olds;
	Public:
		void display()
}; 
Inline void display()
{
	cout << num << endl;

}

如果在类外进行定义的话,那么可以不用进行声明

成员函数的存储方式:c++定义每一个对象所占用的存储空间只是该对象的数据部分所用的空间,并不包括代码段,也就是用一段存储空间来进行存放这个共同的代码,在调用不同的对象的成员函数的时候,都是转去执行这个公共的函数代码段。但是由于调用不同的函数都是执行相同的函数,,c++开发了一种名字为this的指针,每一个成员函数都可以访问这个指针,这个指针是指向这个对象本身的指针,他的值就是被调用的成员函数的对象的首地址。
例如:

class student {
	Private:
		int num;
		int olds;
	Public:
		void display()
		{
			Cout<<this->num<<endl;
		}
};

//这个说的意思的就是说,this指针指向的是当是被调用对象的首地址,这个语句也可以换成 cout<<num<<endl;效果是一样的

构造函数和析构函数
首先,不能够在声明类的时候同时对类的数据成员进行初始化

class student {
	Private:
		int num=0;
		int olds=0;
	Public:
		void display()
		{
			Cout<<this->num<<endl;
		}
};  

如这样写就是错误的
对于类,结构体而言,他们是一种抽象的数据结构,并不会占用存储空间,没有办法容纳数据,如果说一个类的所有数据成员都是共有的,那么在定义对象的时候可以进行对数据成员的初始化。如

Class time {
Public: int a ;
 	Int b ;
	Int c;
};
Time pp={1,2,3};

这样定义对象的初始化是正确的,当然这和之前说过的对结构对象的初始化是一样的,但是如果数据成员是私有的或者保护的,就不能够这样子对对象进行初始化;这种情况就可以用成员函数对私有数据成员进行赋初值操作,这就是下面的构造函数
构造函数作为一种特殊的成员函数,与其他的成员函数不一样,构造函数不需要调用它,是在创建对象的时候自动执行的;
首先 构造函数的名称必须要和类名相同
构造函数的声明为public,没有返回值,因此不需要定义返回类型
构造函数是系统自动调用的,有且只有一次执行机会
构造函数是不可以被继承的(何为继承等会说)
类的定义题中可以有多个构造函数,构造函数也可以是内联函数,可以带参数也可以不带,也可以默认参数
eg:#include
Using namespace std
class time
{
public:
time ()
{
hour = 0;
day = 0;
}
void settime();
void showtime();
private:
int hour;
int day;
};
void time:: showtime()
{
cout << hour << endl;
}
void time::settime()
{
cin >> this->hour >> this->day;
}

Int main()
{
Time mondy;
Mondy.settime();
Mondy->showtime();
Return 0;
}
构造函数没有返回值,所以定义构造函数的时候不需要写他的类型;

带有参数的构造函数
class time
{
public:
time ()
{
hour = 0;
day = 0;
}
void settime();
void showtime();
private:
int hour;
int day;
};
void time:: showtime()
{
cout << hour << endl;
}
void time::settime()
{
cin >> this->hour >> this->day;
}
Time::time(int a,int b)
{
This.hour=a;
This.day=b;
}
除了上面说的带参数的构造函数以外,C++还可以这样定义构造函数
Time (int a,int b) :hour(a),day(b) {}

构造函数的重载
就像函数的重载一样
Class ca
{
public:
Ca();
Ca(int ,int );
Ca(float);
}

拷贝构造函数:简而言之就是将已有对象的整个状态复制到相同类的新对象中
Class ca
{
Public:
ca();
Ca( ca &r(r指的是另外一个对象));
}
就以上面多次用到的time为例
class time
{
public:
time ()
{
hour = 0;
day = 0;
}
//Time(time &r):hour(r.hour),day(r.day){}
void settime();
void showtime();
private:
int hour;
int day;
};
//time ::time(time &r)
{
Hour=r.hour;
Day=r.day;
}
如果在类的定义中没有定义拷贝构造函数,那么系统就会默认自己建立一个拷贝函数,只不过这个函数是将所有值一个一个复制到新的对象当中,在某些特殊的情况下,这种拷贝构造函数就不是很实用了,所以需要自己定义一个拷贝构造函数

析构函数
析构函数有以下特征:
析构函数与类名相同,并且必须要在前面加一个~
析构函数不能够有参数!并且和构造函数一样没有函数类型
一个类有且只有一个析构函数,也就是不可以重载;而构造函数可以重载
对于析构函数的执行过程主要有以下三种情况
在一个函数中定义了一个自动局部对象,当这个函数结束的时候,就会自动的执行析构函数
如果是一个全局变量,当程序的流程离开其作用域的时候,就会调用析构函数
如果用new动态新建了一个对象时,使用delete的时候会调用析构函数
析构函数定义如下:
class time
{
public:
time ()
{
hour = 0;
day = 0;
}
void settime();
void showtime();
~time()
{
Cout<<“I have done my work”<<endl;
}
private:
int hour;
int day;
};
析构函数主要是用来释放资源,当有多个对象该调用析构函数释放资源的时候,采用堆栈得到思想,也就是后定义的先执行析构函数
如下
Int main()
{
Time a();
A.showtime();
Time b();
B.showtime();
Return 0;
}
这个程序的结果如下图

第一个析构函数的输出的B的,第二个析构函数输出是A的

相关特性
引用(&)就是给对象取一个别名,他的作用可以用指针来近似相等,但是它又不等同于指针 指针的被指物地址不一样,是两个不一样的东西;
而引用,是对物体取另外一个名字,他们的地址是一样的。
int e = 9;
int& b = e;
cout << &e << endl;
cout << &e << endl;

对于引用,由于它是别名,所以需要对被引用量先进行赋值操作;并且一旦引用初始化完成后,就不能重新引用到另一个目标上
引用的第一种实例:作为函数的参数
Void swap(int &x,int &y)
{
Temp=x;
X=y;
Y=temp;
} 这样就可以实现两个数的交换
第二种实例:用引用返回值
Int &max(int x,int y)
{
Return( x>y?x:y);
}
Int z=max(a,b)//将a、b中较大的值赋给z
Max(a,b)=20;//将较大值赋值20
Max(a,b)++;//将较大值加一
第三种实例:用引用返回多个参数
int score[4][5] = { {60,70,80,90,78},{75,85,88,78,83},
{89,88,79,96,90},{76,74,69,90,87} };
int& level(int grade[], int uint, int& ga, int& gb);
int main()
{
int genusA = 0, genusB = 0;
int student = 4;
int gradeunit = 5;
for (int i = 0; i < student; i++)
level(score[i], gradeunit, genusA, genusB)++;
cout << “A班人数为” << genusA << endl;
cout << “B班人数为” << genusB << endl;
return 0;
}
int& level(int grade[], int uint, int& ga, int& gb)
{
int sum = 0;
for (int i = 0; i < uint; i++)
sum = sum + grade[i];
sum = sum / uint;
if (sum >= 85)
return ga;
else
return gb;
}

友元
友元关系有三种:以一种是类外定义的普通函数(友元函数)
类外定义的成员函数(友元成员)
类外定义的某一个类(友元类)
因为类的私有成员只能被类的成员函数所访问,友元机制就可以解决类外访问私有成员的问题
友元函数、友元成员以及友元类定义前需要加关键字friend
友元函数
class Time
{
public:
Time(int h, int m, int s);
friend void display(Time t);
~Time();
private:
int hour;
int minute;
int second;
};
Time::Time(int h, int m, int s)
{
hour = h;
minute = m;
second = s;
}
Time::~Time() { cout << “byebye” << endl; }
void display(Time t)
{
cout << t.hour << t.minute << t.second << endl;
}
int main()
{
Time t(10, 13, 56);
display(t);
return 0;
}

友元成员

继承和派生
继承就是新的类从已有的类那里得到的已有的特性
派生就是从已有类产生新类的过程,派生类继承了基类所有的数据成员和成员函数,还可以对成员做必要的增加和调整
已有类称为基类或者父类
继承分为单继承和多继承,也就是子类由一个类继承而来的还是多个类继承而来的
派生类的声明:
Class 派生类名:【继承方式】 基类名 继承方式不写默认为private
在类的继承过程中:
1:对基类的成员全盘接受–除了构造函数和析构函数
2:同名覆盖:就是构造和基类一样的成员函数,直接使用成员名就可以访问到派生类中声明的同名成员
3:可以添加新的成员
对于公有继承,不改变基类在派生类的访问属性
对于私有继承,派生类的对象不可以访问基类中的所有成员
对于应用,经常是公有继承

派生类的构造函数和析构函数:
对于派生类只有一个基类的情况:
派生类名(总的参数表):基类名(参数表)
{
派生类中新增数据成员初始化语句
}
顺序:调用基类的构造函数,执行派生类构造函数体中的内容

有子对象的派生类的构造函数:
也就是说类的数据成员不止有int char等等,还有类

多级派生的构造函数
对于多级而言,不要列出每一层的构造函数,只需要写出他的直接基类的构造函数就可以了
比如
Class a(int a,int b)……
Class a1:public a
A1(int a,int b,intc):a(a,b)
Class a2:public a1
A2(int a,int b,int c,int d):a1(a,b,c)
在执行的时候,调用a2的构造函数,执行的时候,先调用a1的构造函数,执行a1的构造函数的时候,调用a的构造函数;
也就是先执行a的构造函数,然后a1,再然后a2;

对于派生类的析构函数
对于上面的来说;
就是a构造,a1构造,a2构造 a2析构,a1析构,a析构
这是因为类似于堆栈,后进先出原则

多继承
类似于单继承派生类 多继承派生类定义格式如下
Class 派生类名:继承方式 类名,继承方式 类名{ 新增的成员}
构造函数:
派生类名(参数名):类名(参数名),类名(参数名){初始化语句}

class b1
{
public:
b1(int i) { cout << “hhhb1” << i << endl; }

};
class b2
{
public:
b2(int j) { cout << “b2” << j << endl; }

};
class b3
{
public:
b3() { cout << “b3” << endl; }
};
class c :public b1, public b2, public b3
{
public:
c(int i,int j,int c ,int d):b1(i),m(j),n©,b2(d)
{}
private:
b1 m;
b2 n;
b3 mm;
};
int main()
{
c obj(1, 2, 3, 4);
return 0;
}

对于构造函数的使用顺序:
首先按照c类构造函数,先进行b1,b2,b3的构造函数,然后再进行 m n mm的构造函数
所以结果会先显示 b11 b24 b3 b12 b23 b3

由于继承多类,可能会出现二义性
如 c是由a和b派生出来的,但是a和b都有get函数 c obj 对obj使用get函数的时候必须要指明从哪里继承而来的get函数
如 obj.A::get()
同样,如果b1和b2都是由a派生而来的,c是由b1和b2派生而来的,要是想用A的get函数
需要指明是从b1还是b2派生而来的get函数 也就是 c.b1::get()
但是这样的话,c就保存了两份a的成员(可拷贝的),这样增加了数据容量,对访问也构成了干扰
为了解决这个问题,引入了虚基类

对于公共的基类,我们在继承的声明的时候对其进行下列方式的声明
Class 派生类名:virtual 继承方式 基类名
class A
{
public:
int a;
void disp() { cout << “member of a:a=” << a << endl; }
};
class b1 :virtual public A
{
public:
int b1;
};
class b2 :virtual public A
{
public:
int b2;
};
class C :public b1, public b2
{
public:
int c;
void dispc() { cout << c << endl; }
};
int main()
{
C c;
c.a = 2;
c.disp();
return 0;
}

虚基类的初始化过程
对于一般的派生类,不管是直接派生还是多类派生,在定义构造函数的时候,都是对他的直接基类机型赋值
但是对于虚基类的派生问题,定义构造函数的时候,虚基类的成员构造是通过最后一个派生类进行初始化的,
对于基类的其他派生类,调用虚基类的构造函数的时候,都会自动忽略
class A
{
int a;
public:
A(int i) { a = i; }
void disp() { cout << “member of a:a=” << a << endl; }
};
class b1 :virtual public A
{
int k;
public:
b1(int i) :A(i) { }

};
class b2 :virtual public A
{
int l;
public:
b2(int j):A(j){}
};
class C :public b1, public b2
{
public:
C(int x,int y,int z):A(x),b1(y),b2(z){}
};
int main()
{
C c(1,2,3);
c.disp() ;
return 0;
}

就像这个程序,对于虚基类的构造,在B1和B2的时候都会自动跳过

多态:例如同一个加法符号,可以进行不同类型变量的相加

虚函数的定义与使用:
定义如下:
Virtual 类型 函数名 (参数表)
当一个成员函数被声明为虚函数的时候,他的派生类的同原型函数都会自动成为虚函数
所以在派生类重新声明这个函数的时候,可以加virtual也可以不加
通过虚函数和指向基类对象的指针变量的配合使用,就可以方便的调用同一类族的不同类的原型函数

为什么要使用虚函数?
主要是对于动态类型:
比如你先new一个对象,通过指针来运行
如果不定义成虚函数的话,那么基类指针不会指到派生类的
比如 A是基类,display()是他的虚函数(public
B是A的派生类,对display重新进行定义
现在A *PTR=NEW B
ptr=&B
ptr->display()

就可以调用B的display函数
如果说display不是虚函数的话,那么上面的语句执行的是A 的display

同样的也会有虚析构函数
为什么会有虚析构函数,同样的你在new一个对象的时候,不用的时候需要将它delete
A *ptr=NEW b
当用完的时候,需要delete
Delete ptr
如果A的析构函数不是虚析构函数,那么只会deleteA ,并不会deleteB

说明一下为什么要有虚构造函数 其实主要是因为指针类型 上文的指针类型用的是基类的,如果用的是派生类的类型,那么就没有函数调用的影响,但是会过多地new delete 浪费空间
纯虚函数
就是virtual 类型 函数名(参数) =0;
这样呢,就是在基类里面不进行定义,只是有一个名字而已,在派生类中进行定义

模板和标准模板库
模板包括有函数模板和类模板 一个函数模板惊参数实例化后可以生成多个进数据类型不同的模板函数
同样,一个类模板经过参数实例化后也可以生成许多仅数据类型不同的模板类,每一个模板类可以定义各自的对象
模板的定义格式如下:template <模板参数表>
模板定义体
函数模板:
例如 定义swap函数
Void swap(int & n1,int &n2)
{
Int temp;
Temp=n1;
N1=n2;
N2=temp;
}
Void swap(float & n1,float &n2)
{
float temp;
Temp=n1;
N1=n2;
N2=temp;
}
Void swap(long & n1,long &n2)
{
long temp;
Temp=n1;
N1=n2;
N2=temp;
}
尽管是函数的重载,但是每次都需要再次定义
采用函数模板 他不是一个实际的餐阿叔,只是对函数的描述,免疫程序不会为其产生任何代码
例如 template <模板参数表>
返回类型 函数名(参数表)
{
函数定义体
}
例如: template
Void swap(t &n1,t &n2)
{
T temp;
Temp=n1;
N1=n2;
N2=temp;
}
这里面的class代表的是“任意类型”
那么定义晚模板怎么样运行
很简单:int ia=1,ib=2;
double ic=3,id=4;
Swap (ia,ib);
Swap(ic,id);
在c++中,函数模板和函数名是可以一样的,在调用的时候遵循
先寻找参数完全一样的重载函数,如果没有在进行寻找函数模板将其实例化,还是没有就通过类型转换产生参数匹配;
如果都没有的话,error
template
T max(T x, T y)
{
return x > y ? x : y;
}
const char* max(const char* x, const char* y)
{
return strcmp(x, y) ? x : y;
}
int main()
{
int a = 1, b = 3;
char *m = “hello”;
char *n = “hell”;

cout << max(a, b) << endl;
cout << max(m, n) << endl;
return 0;

}

类模板
类模板的定义和函数模板定义大致相同
Template <模板参数表>
Class 类名
{
类模板体定义;
}

Template <class t1,class t2>
Class example
{
Public:
T1 getx(){return x};
T2 gety(){return y};
Private:
T1 x;
T2 y;
};
在进行调用的时候
Example<int,double> example1;

template
class pp
{
public:
pp(int slots = 1)
{
size = slots;
aptr = new t[slots];
}
void fill_array();
void disp_array();
~pp() { delete[]aptr; }
private:
int size;
t* aptr;
};
template
void pp::fill_array()
{
cout << “输入”<<size<<“个数据” << endl;
for (int i = 0; i < size; i++)
{
cout << “请输入第” << i+1 << “个数据”;
cin >> aptr[i];
}
}
template
void pp::disp_array()
{
for (int i = 0; i < size; i++)
{
cout << aptr[i] << " ";

}
cout << endl;

}
int main()
{
pp ac(3);
cout << “请填充一个字符数组”;
ac.fill_array();
cout << “数组的内容是” << endl;
ac.disp_array();
pp ad(3);
cout << “请填充一个浮点数组” << endl;
ad.fill_array();
cout << “浮点数组为” << endl;
ad.disp_array();
return 0;
}

发布了10 篇原创文章 · 获赞 7 · 访问量 1196

猜你喜欢

转载自blog.csdn.net/qq_43294951/article/details/101479471
今日推荐