zoukankan      html  css  js  c++  java
  • C++远征离港篇-学习笔记

    C++远征离港篇

    离港总动员

    C++远征计划的学习者肯定是冲着封装,继承,多态来的。

    mark

    知识点:

    • 指针 VS 引用
    • #define VS const(更强数据控制力)
    • 函数默认值 & 函数重载
    • 内存管理(头疼): 堆中的内存管理几乎完全由程序员操心[出来混总是要还的]
    • 封装 继承 多态

    c++语言引用

    引用类型:

    • 什么是引用?

    mark

    引用就是变量的别名

    • 能不能只有别名?

    只有别名,别名就变成了真实姓名.只有别名也是无法进行命名的。

    基本数据类型的引用

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    int main(void)
    {
    	int a = 3;
    	// 给a起了一个别名b
    	int &b = a; //引用必须初始化
    
    	b = 10; // 给b赋值10,a的值也就由3变为10
    	cout << a << endl;
    
    	system("pause");
    	return 0;
    }
    

    为a起别名b: 对别名做的操作就是对a本身做了操作[叫小萝卜头干什么,罗某某也干了什么]

    结构体类型的引用

    使用别名对于结构体做操作的例子:

    typedef struct
    {
    	int x;
    	int y;
    }Coor;
    
    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    typedef struct
    {
    	int x;
    	int y;
    }Coor;
    
    int main(void)
    {
    	Coor c1;
    	Coor &c = c1; // 给c1起了别名c
    	c.x = 10; // 使用别名对真实值做操作
    	c.y = 20;
    	cout << c1.x << endl << c1.y << endl;
    
    	system("pause");
    	return 0;
    }
    

    指针类型的引用

    类型 *&指针引用名 = 指针;
    
    #include <iostream>
    using namespace std;
    int main(void)
    {
    	int a = 10;
    	int *p = &a; // 定义指针p
    	int *&q = p; // 指针p的别名q
    	*q = 20;
    	cout << a << endl;
    	system("pause");
    	return 0;
    }
    
    • int a = 10; // 给a分配一个内存逻辑地址,如0x100001。这个地址存放了值10;
    • int *p = &a; //创建指针变量p指向a,给p分配地址0x100002,这个地址存放的值是"0x100001"(a的逻辑地址值);
      -int *&q = p; // (给指针p起别名q)创建变量q,给q分配地址也是0x100002, 因此这个地址存放的值还是a的逻辑地址值;
    • *q = 20; // (对q做操作)访问存放在q变量地址下的值,获得了a的地址值, 再访问一下a的地址值,修改里面存放的内容为20;

    引用作为函数参数

    C语言中将两个数的值进行交换:

    void fun(int *a, int *b)
    {
    	int c =0;
    	c =*a;
    	*a =*b;
    	*b =c;
    }
    int main()
    {
    	int x =10;
    	int y =20;
    	fun(&x,&y);
    	return 0;
    }
    

    c++中引用实现:

    void fun(int &a, int &b)
    {
    int c =0;
    c =a;
    a =b;
    b =c;
    }
    int main()
    {
    	int x=10,y=20;
    	fun(x,y)
    	return 0;
    }
    

    a是x的别名。b是y的别名。 里面操作的就是实际的参数了。

    C++语言引用代码演示:

    基本数据类型引用示例:

    2-2-C++Two-ReferenceDemo/main.cpp

    #include <iostream>
    #include <stdlib.h>
    using namespace std;
    
    int main(void)
    {
    	int a = 10;
    	int &b = a; // 定义一个引用(别名)
    	// int &b = NULL; 计算机会报错, 初始化 无法从 int 转换为 int &
    
    	b = 20;
    	cout << a << endl;
    
    	a = 30;
    	cout << b << endl;
    	system("pause");
    	return 0;
    }
    

    mark

    对于本体和别名的操作具有相同的效果。

    结构体引用示例:

    2-2-2-C++Two-ReferenceStructDemo/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    typedef struct
    {
    	int x;
    	int y;
    }Coord; //Coord 坐标
    
    int main(void)
    {
    	Coord c;
    	Coord &c1 = c; // 起别名c1
    	c1.x = 10;
    	c1.y = 20;
    	cout << c.x << endl << c.y << endl;
    
    	system("pause");
    	return 0;
    }
    

    mark

    指针引用示例:

    2-2-3-C++Two-ReferencePointerDemo/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    int main(void)
    {
    	int a = 3;
    	int *p = &a;
    	int *&q = p; // 指针p的别名q
    
    	*q = 5;
    
    	cout << a << endl;
    	system("pause");
    	return 0;
    }
    

    mark

    函数参数引用示例:

    2-2-4-C++Two-ReferenceFunctionParameter/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    void fun(int &a, int &b);
    int main(void)
    {
    	int x = 10;
    	int y = 20;
    
    	cout << x << endl;
    	cout << y << endl;
    	fun(x, y);
    	cout << "交换后:" << endl;
    	cout << x << endl;
    	cout << y << endl;
    	system("pause");
    	return 0;
    }
    
    void fun(int &a, int &b)
    {
    	int c = 0;
    	c = a;
    	a = b;
    	b = c;
    }
    

    mark

    看起来传入的是实参x,y 实际上 a是x的引用,b是y的引用

    int a; 
    int &b = a;
    int &c = a;
    

    一个本体可以起多个别名

    单元巩固

    定义一个引用y,y是x的引用,然后打印x和y的值。将y的值更改之后再次打印,x和y的值。

    2-2-5-C++Two-ReferenceUnitDemo/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    int main(void)
    {
    	int x = 3;
    	//定义引用,y是x的引用
    	int &y = x;
    	//打印x和y的值
    	cout << x << endl;
    	cout << y << endl;
    	//修改y的值
    	y = 5;
    	//再次打印x和y的值
    	cout << "After Change Y:" << endl;
    	cout << x << "," << y << endl;
    	system("pause");
    	return 0;
    }
    

    mark

    C++语言-const

    const关键字是用来控制变量是否可以变化的。

    const与基本数据类型

    没有const之前的情况:

    int x =3; //变量
    

    mark

    const int x=3;  //此时的x为常量
    

    mark

    x变成了一个常量,无法进行更改。再赋值其他数字,编译时就会报错。

    const与指针类型

    • const int *p=NULL; 完全等价于int const *p=NULL

    mark

    int *const p=NULL与前两种有区别。

    • 除了常规上面加一处,也可以在前面后面都加

    const int * const p = NULL; 完全等价于 int const * const p = NULL;

    mark

    int x =3;
    const int *p = &x;
    // p = &y;正确
    // *p = 4;错误
    

    const 修饰*p; 因此p可以指向其他的地址,但*P不可以被改变。

    变量名 存储地址 存储内容
    x &x 3
    p &p &x
    int x =3;
    int *const p = &x;
    // p = &y;错误
    // *p = 4;正确
    

    const写在了*的后面。const 修饰p; const修饰的p只能指向一个地址。

    变量名 存储地址 存储内容
    x &x 3
    p &p &x (不可改变)

    const 修饰p;

    const int x =3;
    const int *const p = &x;
    // p = &y; 错误
    // *p = 4; 错误
    
    变量名 存储地址 存储内容
    x &x 3 (不可改变)
    p &p &x (不可改变)

    const 与引用

    int x =3; 
    const int &y =x;
    // x=10; 正确
    // y=20; 错误 y作为别名加了const
    
    变量名 存储地址 存储内容
    x &x 3

    const 实例

    //错误
    const int x =3;
    x =5;
    // 常量x不能进行赋值了
    
    //错误
    int x =3;
    const int y =x ;
    y = 5;
    // y 变成了常量,不能再赋值
    
    //错误
    int x =3;
    const int *y =&x; // 修饰*y
    *y = 5; // *y不可变化
    
    //错误
    int x =3,z=4;
    int * const y = &x;
    y = &z; // 修饰y 不允许重新指向
    
    //错误
    const int x =3;
    const int &y =x;
    y =5;
    
    //错误:指针会存在改变常量的风险。
    const int x =3;
    int *y = &x; // x不可变,指针可变。 
    // 使用一个可变的指针,指向一个不可变的变量。风险是可以通过*y的方式改变x的值。
    // 编译器会禁止
    
    //正确。x拥有读写,y只可读。
    int x =3;
    const int *y =&x; //权限小的接收权限大的
    

    const代码示例

    3-2-1-constIntChangeDemo/main.cpp

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    int main(void)
    {
    	const int x = 3;
    	x = 5;
    
    	system("pause");
    	return 0;
    }
    

    1> error C3892: “x”: 不能给常量赋值

    通过define和const修饰的都可以达成设置常量目的。

    • const的优点是,常量有类型,在编译的时候要检查语法错误。
    • 而#define定义的没有数据类型,是宏定义在编译时不再检查语法错误
    • 推荐用const来定义常量

    3-2-2-constPointerChangeDemo/main.cpp

    #include <iostream>
    using namespace std;
    int main(void)
    {
    	int x = 3;
    	int y = 4;
    	int const *p = &x; // const int *p = &x等价
        // 都是修饰*p的
    
        //*p = 5;
    	x = 5;
    	p = &y;
    	cout << *p << endl;
    	system("pause");
    	return 0;
    }
    

    此时*p的值不能进行修改。但是可以修改p指针指向的地址

    错误	C3892	“p”: 不能给常量赋值
    

    mark

    3-2-3-constPointerChangeDemo2/main.cpp

    #include <iostream>
    using namespace std;
    int main(void)
    {
    	int x = 3;
    	int y = 5;
    	int *const p = &x; // const修饰p
        // p = &y;  // p不能给常量赋值
    	*p = 10;
    	cout << x << endl;
    	system("pause");
    	return 0;
    }
    

    此时对于p指向的地址不能修改。但是对于*p的值可以进行修改。

    mark

    3-2-4-constPointerMoveDemo/main.cpp

    #include <iostream>
    using namespace std;
    int main(void)
    {
    	int x = 3;
    	int y = 5;
    	int const *p = &x;
    	cout << *p << endl;
    	p = &y;
    	//*p = 10;
    	cout << *p << endl;
    	system("pause");
    	return 0;
    }
    

    因为此时const修饰的*p,而p是可以移动到其他地址。

    mark

    const修饰一个引用:

    3-2-5-constReferenceDemo/main.cpp

    #include <iostream>
    using namespace std;
    int main(void)
    {
    	int x = 3;
    	int y = 5;
    
    	int const &z = x;
    	// z = 10;  // z不能被改变
    	x = 20;
    	cout << x << endl;
    	system("pause");
    	return 0;
    }
    

    mark

    别名被限制上了不能修改,但是原变量是可以修改的。

    函数中的const

    因为可以保证传入函数内部的值不会因为误操作而修改原有值

    3-2-6-constFunctionDemo/main.cpp

    #include <iostream>
    using namespace std;
    
    void fun( const int &a,  const int &b);
    
    int main(void)
    {
    	int x = 3;
    	int y = 5;
    	fun(x, y);
    	cout << x << "," << y << endl;
    	system("pause");
    	return 0;
    }
    
    void fun( const int & a, const int & b)
    {
    	// 错误因为传入的值为const。不能进行修改。
    	a = 10;
    	b = 20;
    }
    

    mark

    把const去掉,因为传入的是引用,所以原始值可以被修改。

    而当const修饰之后,传入函数内部的值并不会修改原有值

    关于const用法,以下错误的是:

    A. int const a = 3; int *p = &a; // 
    B. int a = 3; int const *p = &a;
    C. int a = 3; int * const p = &a;
    D. const int a = 3; int const &b = a;
    

    B const 修饰*p C const 修饰 p D const修饰a的别名b
    mtianyan:指针指向const修饰的变量时,应该是const int const *p = &a;

    单元巩固

    使用const关键字定义整型变量count,并定义指针p引用变量count。利用for循环打印count次Hello C++

    3-4-C++-UnitDemo/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    int main(void)
    {
    	// 定义常量count
    	const int count = 3;
    	int const *p = &count;
    	// 打印count次字符串Hello C++
    	for (int i = 0; i < *p; i++)
    	{
    		cout << "Hello C++" << endl;
    	}
    	system("pause");
    	return 0;
    }
    

    因为只对*p进行了const。因此可以让p指向其他地址。

    c++函数新特性

    函数参数默认值

    void fun(int i, int j=5, int k=10); // j,k有默认值
    void fun(int i, int j=6, int k); // 错误写法
    

    有默认参数值的参数必须在参数表的最右端

    声明写默认值,定义不建议写。 定义时写默认值有些编译器无法通过。

    void fun(int i, int j = 5, int k = 10);
    
    void fun(int i, int j, int k)
    {
    	cout << i << j << k;
    }
    

    使用时:

    int main()
    {
    	fun(20);
    	fun(20,30);
    	fun(20,30,40);
    }
    

    无实参则用默认值,否则实参覆盖默认值

    函数重载

    前提: 在相同作用域下

    两个条件:

    • 用同一个函数名定义的多个函数
    • 参数个数参数类型不同

    demo代码:

    int getMax(int x, int y, int z)
    {
    	//TO DO
    }
    double getMax(double x ,double y)
    {
    	//TO DO
    }
    

    思考:编译器如何识别重载的函数

    实际的编译之后,名称+参数形成新的函数。来区分两个所谓的同名函数。

    • int getMax(int x, int y, int z) ->getMax_int_int_int
    • double getMax(double x ,double y) ->getMax_double_double

    调用时,则根据实参类型和个数自动识别。

    重载的好处:

    • 求几个数最大值,比如有时候求三个数有时候求5个数,有时候求整数,有时候求浮点数。不需要想名字,计算机帮我们决定。

    内联函数

    ![内联函数](//upload-images.jianshu.io/upload_images/1779926-5cb6ebca6707f613.png?imageMogr2/auto-orient/
    strip%7CimageView2/2/w/1240)

    主调函数调用普通函数有五个步骤:

    1. 调用fun(),2. 找到fun()的相关函数入口 3. 执行fun() 中的相关代码 4. 返回主调函数
    1. 主调函数向下运行其他代码直到结束。

    内联函数会在编译时将函数体代码和实参代替函数调用语句。

    • 省掉了2和4步骤,会节省时间,尤其是循环调用。

    内联函数关键字inline

    inline int max(int a,int b,int c);
    
    int main()
    {
    	int i =10,j=20,k=30,m;
    	m = max(i,j,k);
    	cout<<"max="<<m<<endl;
    	return 0;
    }
    

    使用时和普通函数一样使用。代码展开后相当于代码粘贴进来。

    mark

    mark

    思考: 为什么不所有地方都使用内联函数?

    • 内联编译是建议性的,由编译器决定
    • 逻辑简单(最好不要包含for循环等),调用频繁的函数建议使用内联
    • 递归函数无法使用内联方式。

    内容总结

    1. 函数参数默认值: 实参覆盖默认值
    2. 函数重载: 名称相同,参数可辩 (个数类型)
    3. 内联函数: inline 效率高 有条件(1.逻辑简单,2.不能是递归)

    C++函数特性代码演示

    学习函数默认参数,重载,内联函数。

    • 函数参数默认值:

    4-2-c++-functionDefaultParameter/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    void fun(int i=30, int j = 20, int k = 10);
    
    int main(void)
    {
    	fun();
    	fun(100);
    	fun(100, 200);
    	fun(100, 200, 300);
    	system("pause");
    	return 0;
    }
    
    void fun(int i, int j, int k)
    {
    	cout << i << "," << j << "," << k << endl;
    }
    

    mark

    已传入的实参覆盖默认值,未传入的使用默认值。 默认值从右侧开始赋值,声明时赋默认值,定义时不要默认值。

    函数重载

    前提条件,函数在同一个作用域下,默认多个函数在同一个命名空间时。

    当没有定义命名空间时,函数同名就默认是重载了。

    4-2-C++-FunctionOverload/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    void fun(int i = 30, int j = 20, int k = 10);
    void fun(double i = 30.0, double j = 40.0);
    int main(void)
    {
    	// fun(); //“fun” : 对重载函数的调用不明确
    	// 	有多个 重载函数 "fun" 实例与参数列表匹配
    
    
    	fun(1, 2);
    	fun(1.1, 2.2);
    
    	system("pause");
    	return 0;
    }
    
    void fun(int i, int j, int k)
    {
    	cout << i << "," << j << "," << k << endl;
    }
    
    void fun(double i, double j)
    {
    	cout << i << "," << j << endl;
    }
    

    mark

    fun() 两个函数都可以,因此编译器懵了。

    inline函数实现只需要加上inline关键字

    inline void fun(int i = 30, int j = 20, int k = 10);
    inline void fun(double i = 30.0, double j = 40.0);
    

    inline这种内联,只是一种编译方式,结果上没有什么不同。

    C++的重载的两个函数参数数量可以相同也可以不同, 当参数数量相同时,只需要对应参数类型不同即称为重载。

    单元巩固: 代码练习

    4-4-ReturnMaxDemo/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    /**
    *函数功能:返回a和b的最大值
    *a和b是两个整数
    */
    int getMax(int a, int b)
    {
    	return a > b ? a : b;
    }
    
    /**
    * 函数功能:返回数组中的最大值
    * arr:整型数组
    * count:数组长度
    * 该函数是对上面函数的重载
    */
    int getMax(int arr[], int count)
    {
    	//定义一个变量并获取数组的第一个元素
    	int a = arr[0];
    	for (int i = 1; i < count; i++)
    	{
    		//比较变量与下一个元素的大小
    		if (a <arr[i])
    		{
    			//如果数组中的元素比maxNum大,则获取数组中的值
    			a = arr[i];
    		}
    	}
    	return a;
    }
    
    int main(void)
    {
    	//定义int数组并初始化
    	int numArr[3] = { 3, 8, 6 };
    
    	//自动调用int getMax(int a, int b)
    	cout << getMax(6, 4) << endl;
    
    	//自动调用返回数组中最大值的函数返回数组中的最大值
    	cout << getMax(numArr, 3) << endl;
    	system("pause");
    	return 0;
    }
    

    mark

    C++内存管理

    什么是内存管理?

    思考:内存的本质是什么?

    内存的本质是一种资源,由操作系统掌控。

    我们能做什么?

    我们可以对内存进行申请和归还操作,申请/归还内存资源称为内存管理。

    内存的申请与释放

    运算符: new delete

    • 内存的申请:int *p=new int;
    • 释放:delete p;

    这是申请和释放某一个内存

    申请和释放块内存

    int *arr=new int[10]; // 申请块内存
    delete []arr; // 释放快内存
    

    内存操作注意事项

    回忆: 申请和释放内存的其他方式

    • c语言中:
    void *malloc(size_t size); // 使用申请内存函数
    void free(void *menblock); // 使用释放内存函数
    
    • c++: new delete 运算符

    配套使用不要混搭

    • 申请内存是否一定成功: 不一定会有那么多内存.
    int *p=new int [1000];
    
    if(NULL==p)
    {
    	//内存分配失败
    }
    

    释放内存注意:

    • 在释放内存后,要将指针值赋为空
    int *p=new int [1000];
    
    if(NULL==p)
    {
    	//内存分配失败
    }
    
    delete []p;
    p = NULL;
    int *p=new int;
    
    if(NULL==p)
    {
    	//内存分配失败
    }
    
    delete p;
    p = NULL;
    

    不置为空,它就会指向刚才那块内存。我们如果再次使用delete,就会造成同一块内存回收两次。
    计算机会出现异常。

    内容总结:

    使用new申请内存,使用delete释放内存,配套使用。

    申请内存需要判断是否失败。释放内存要记得指针置空。

    new和delete配套使用

    内存管理代码演示

    5-2-NewDeleteMemoryManage/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    int main(void)
    {
    	//int *p = new int(20); //申请同时初始化
    	
    	int *p = new int;
    	if (NULL == p)
    	{
    		system("pause");
    		return 0;
    	}
    
    	*p = 20;
    	cout << *p << endl;
    
    	delete p;
    	p = NULL;
    
    	system("pause");
    	return 0;
    }
    

    mark

    申请块内存:

    5-2-2-BlockMemoryManage/main.cpp

    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    int main(void)
    {
    	int *p = new int[1000];
    	if (NULL == p)
    	{
    		system("pause");
    		return 0;
    	}
    
    	p[0] = 10;
    	p[1] = 20;
    	cout << p[0] << "," << p[1] << endl;
    
    	delete []p; // 注意这里的[],否则只会释放第一块。
    	p = NULL;
    	system("pause");
    	return 0;
    }
    

    mark

    单元巩固

    在堆中申请100个char类型的内存,拷贝Hello C++字符串到分配的堆中的内存中,打印字符串,最后释放内存。

    5-4-StrcpyMemoryMange/main.cpp

    #include <string.h>
    #include <iostream>
    using namespace std;
    int main(void)
    {
    	//在堆中申请100个char类型的内存
    	char *str = new char[100];
    	if (NULL == str)
    	{
    		system("pause");
    		return 0;
    	}
    	//拷贝Hello C++字符串到分配的堆中的内存中
    	strcpy_s(str,100,"Hello C++");
    	//打印字符串
    	cout << str << endl;
    	//释放内存
    	delete[]str;
    	str = NULL;
    	system("pause");
    	return 0;
    }
    

    没有与参数列表匹配的 重载函数 "strcpy_s",添加char数组的长度。

  • 相关阅读:
    使用SQL语句对表进行分页查询
    [转]Message Crack Wizard for Win32 SDK Developer
    笔记——《C语言也能干大事》之对话框程序代码
    如何通过C#调用CHM帮助文件,显示到指定页面
    winform 数字输入控件初稿
    用JAVA在读取EXCEL文件时如何判断列隐藏
    css中height:100%不起作用的解决方法 项目个案
    枚举(enum)的常用操作
    转 VirtualBox“please use a kernel appropriate for your cpu”
    Asp.NET导出Excel文件乱码 终极解决方法
  • 原文地址:https://www.cnblogs.com/mtianyan/p/9359739.html
Copyright © 2011-2022 走看看