【C++】day02 - 【内联函数】【参数哑元】【参数的默认值】【c++ 动态内存分配new()】【c++中的引用】【c++中的四种类型转换运算符】【c++之父给c程序员的建议】

一、内联函数

1.1内联函数概述

	背景知识:
		函数调用的实质是,将程序执行的顺序转移到被调用的函数所在
		的内存地址,将函数执行完后,再返回到原来的地址继续往下执行,
		因此需要保护现场并记忆执行的地址,还要恢复现场。即不断地有
		函数入栈,会造成栈空间或栈内存的大量消耗。栈空间是指放置函
		数内数据的内存空间,是有限的,假如频繁大量的使用就会造成因栈
		空间不足所造成的程序出错问题。而且,函数调用需要消耗一定的
		时间和空间,于是影响了效率。
	内联函数概念:
		内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在
		每一个调用处。

1.2如何实现

	正常定义一个函数,然后在前面加上inline
		inline int getmax(int x,int y){
			retyrn x>y?x:y;
		}

1.3内联函数适用场景

	小函数(几行、几十行的)、大频率地调用 则适合内联;
	大函数、稀少调用   则不适合内联;
	递归函数不能内联;
	inline只是一种请求,请求成功则皆大欢喜按照内联方式
		若请求不成功则按照普通函数调用。

二、参数哑元

2.1概念

	如果一个函数参数只有类型而没有参数名,则叫哑元。

2.1作用

	①让参数列表匹配更加严格;
	②保持函数的向前兼容;
		如前人写的函数为void decode(int pkey);
		而你很厉害,改进了这个函数,你用不到形参pkey了,
		但是又为了保留以前的调用习惯而保留形参(当然你不用)
		,则你可以写为void decode(int);
	③区分函数
		前++
			operator++();//代表前++
		后++
			operator++(int);//代表后++
		(以后在运算符重载再讲)

三、参数的默认值

3.1概念

	如果一个函数的参数设置了默认值,则调用这个参数时可以
	不传值,这时使用的值就是默认值。如果对这个参数传入值,
	则传入的值会替代掉默认值。

3.2语法

	①语法一
		函数的默认值必须靠右,比如下面的语法可以:
		void foo(int a,int b,int c = 0);
		而这个就不可以:
		void foo(int a = 1,int b,int c = 0);
	②语法二	
		函数的声明和函数的实现分开时,默认值在声明时指定

3.3程序举例

#include <iostream>
using namespace std;

int getmax(int x = 100, int y =1);
int main(){
    
    
	cout << getmax() << endl;
	cout << getmax(2) << endl;
	cout << getmax(1,101) << endl;
	/*运行结果:
	100
	2
	101
	*/
	
}
int getmax(int x, int y){
    
    
	return x>y?x:y;
}

四、c++ 动态内存分配new()

背景知识:malloc的含义是“给我一个大小为size的连续内存”,
而calloc是“给我n个大小为size的内存”。

①new(相当于c中的malloc())   、 delete
	在堆中动态的申请内存。
		类型 *指针名 = new 类型;
		如:int *pi = new int;//说明你申请int即4字节空间
		如:int *pi2 = new int(100);
		/*意为:申请int型4字节空间,并将数值100赋值到该空间*/
	补充,无论是new,还是malloc,都是申请连续的内存空间,也就
	是说,你同样也可以把一个结构体放到这个内存空间。如:
		struct Data{int a;double b};
		Data *p = new Data;

②new[](相当于c中的calloc())  delete[]
	即申请 多个 对象的空间	
		类型 *parr = new 类型[n];
	程序举例:
#include <iostream>
using namespace std;


int main(){
    
    
	/*申请5个整数的堆空间*/
	int *parr = new int[5];

	/*给这5个整数赋值,并输出*/
	for(int i = 0; i<5; i++){
    
    
		cin >> *(parr+i);
		/*另一种方法:写为parr[i]也行!*/
	}
	for(int i = 0; i<5; i++){
    
    
		cout << *(parr+i);
	}
	/*本程序注意,为了给5个数赋值,不可以写为*parr++,
	因为:
	和上面的两种方法不同,你这样你就操作指针了,你直接把指针
	的位置给动了。
	一个经验是:我们迫不得已操作指针时,为了避免破坏原来指针的
	位置,通常我们把指针赋值给另一个变量,我们去操作那个变量*/

	/*释放这块内存*/
	delete[] parr;
}
③一个知识点
	我们知道new申请的内存在堆中,我们可不可以把这个堆中的空间
		挪到栈中?答案是可以的。
	char data[100];//在栈中申请100个字节内存
	int *pa = new (data) int[25];//在data中申请100个字节。
	并且你要知道的是:pa和data这两个地址是一样的:
	cout << pa << endl;
	cout << (void*)data << endl;/*data是指针,但是c++容易
								把它当成字符串*/

五、c++中的引用

5.1概念

	引用就是别名,如:
		9527 华安 唐伯虎 唐寅
		机器猫 叮当 哆啦A梦

5.2语法

	int var_x = 9527;
	//我们给9527起个别名
	/*rvar_x就是变量var_x的别名。
	定义别名必须初始化;
	引用一旦初始化,则在整个程序运行中为这个变量var_x服务*/
	int& rvar_x = var_x;

5.3程序举例

#include <iostream>
using namespace std;


int main(){
    
    
	int var_x = 9527;
	int& rvar_x = var_x;//定义一个引用,并初始化引用var_x
	cout << var_x << endl;//9527
	cout << rvar_x << endl;//9527
	rvar_x = 9526;
	cout << var_x << endl;//9526

	/*当然我们可以对var_x起多个别名*/
	int& rrvar_x = var_x;
	
	/*引用一旦初始化,则引用的对象终生不能改变*/
	/* 显然,下面这样写是错的:
		int y = 1000;
		int& rvar = y;
	*/
	/*但是这样写没问题:
	(这只是赋值,不是引用)*/
		int y = 1000;
		rvar = y;

}

5.4引用的简单实现(原理)

	背景知识:const修饰变量,功能是对变量声明为只读特性,
		并保护变量值以防被修改。
		
	具体实现:
		类型 *const 指针名;
	代码:
#include <iostream>
using namespace std;

int main(){
    
    
	int var_t = 9577;
	
	/*const修饰的指针pi可以改变指向,但不能通过pi修改值*/
	int const *pi = &var_t;
	int var_y = 100;
	pi = &var_y;//改变pi指向
	//*pi = 10;这是错的,不可以修改

	/*好,看看引用的底层实现*/
	int * const rpi = &var_t;
	//rpi = &var_y;这是错的,不能改变rpi指向
	*rpi = var_y;//但可以修改指针所指向的值

		}

5.5引用的应用

	①函数的参数
		引用传递(c++) vs 值传递(c语言)
		引用传递
			减少拷贝
			在函数内部修改函数外部的数据
		写一个交换值的例子:
#include <iostream>
using namespace std;
void myswapa(int x, int y){
    
    
	int temp = x;
	x = y;
	x = temp;
}
void myswapb(int *x, int *y){
    
    
	int temp = x;
	x = y;
	x = temp;
}
void myswapc(int& x, int& y){
    
    
	int temp = x;
	x = y;
	x = temp;
}

/*const可以防止函数内部对x修改;
增加函数兼容性,可以传const修饰的变量和字面量*/
void printVar(const int& x){
    
    
	cout << "x=" << x << endl;
	
}
int main(){
    
    
	int x = 20;
	int y = 123;

	/*方法一:失败(不可行)*/
	myswapa(x,y);

	/*方法二:成功*/
	myswapb(&x, &y);

	/*方法三:成功*/
	myswapc(x, y);

	/*总结:					方法一和方法二都是c用法,是值传递。方法一是失败的;
		方法三用的引用,即对main()中的x、y起了别名为myswapc中的x、y
	*/
	
	printVar(x);
	printVar(100);//printVar有const,因此可以打印常量

}
	②函数的返回值	
		函数的返回值只能作为右值,不能作为左值。如:
			int z = getmax(x,y);//把函数返回值赋给z,函数
								在等号右侧。
		如果希望函数的返回值作为左值,则可以使用引用类型。
		看程序:
#include <iostream>
using namespace std;

int& getmax(int&x, int&y){
    
    
	return x>y?x:y;
}
int main(){
    
    
	int x = 20;
	int y = 123;
	int& z = getmax(x, y);

	/*让getmax()作为左值*/
	getmax(x,y) = 1000;//把1000赋给getmax()的返回值y,则z=y = 1000
	cout << z << endl;//1000
	cout << y << endl;//1000

}
5.5思考指针和引用的练习和区别?

六、c++中的四种类型转换运算符

6.1 static_cast<类型>(变量)

	在某一个方向上可以做隐式类型转换。
	程序举例:
 #include <iostream>
using namespace std;

int main(){
    
    
	int *pi = new int(100);
	void *pv = pi;

	//想要输出pv所指的值,就得把其转换为整型指针
	int *ti = static_cast<int *>(pv);
	cout << *ti << endl;
}

6.2 dynamic_cast<类型>(变量)

	适合多态性的父子类之间
	(本小节以后再讲)

6.3 const_cast<类型>(变量)

	作用:去掉const修饰
	程序举例:
#include <iostream>
using namespace std;

int main(){
    
    
	const int x = 1000;
	int *pi = const_cast<int*>(&x);/*我们知道,x是const修饰的,是不能改的
							但是我们就想改。
							你可以通过指针来改,但是前提是你得去掉x的const修饰*/
	*pi = 99;
	
	cout << x << endl;/*x=100。按理说x应该是99啊,但是
	c++有个毛病,被const修饰过的x,c++编译器会把所有
	的x当成100。
	理一下思路:x被const修饰,不可更改。
	但被const_cast<int*>(&x)之后去掉了const,按理说能更改了
	但是c++编译器把所有的x当成100,怎么办?
	答案是,把const int x = 1000;改为
		volatile const int x = 1000;
		这样一来x就等于99了。
	*/
	cout << *pi << endl;//99 
}

volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

6.4 reinterpret_cast<类型>(变量)

		重新解释内存,最接近C的 强制类型转换。
		可以把整数变成指针
		也可以把指针变成指针

七、c++之父给c程序员的建议

1、尽量少使用宏,可以使用枚举和const定义常量
	用inline替代掉带参的宏
	使用namespace避免命名冲突
2、变量随用随时定义,以保证变量的初始化
	如:for(int i=0;i<5;i++){}
3、尽量避免使用c语言的强制类型转换,如果要进行类型转换,
	尽量使用C++提供的四个转换运算符.
4、多用new delete少用malloc free
5、少使用c风格的字符串,多使用string类型
6、逐渐建立面向对象的编程思想。

猜你喜欢

转载自blog.csdn.net/weixin_45519751/article/details/108103915