Day10(上).C++基础强化

首先我们先回忆一下。

程序设计方法的发展历程

面向过程的结构化程序设计方法:

        自顶向下、逐步求精

面向对象的方法:

        封装、继承、多态

        把对象的属性和操作封装起来,形成一个类;类是一个抽象的概念;class

一句话:每一个程序员都不能固步自封,要多接触新的行业、新的技术领域。崭新的突破自我。

namespace命名空间:

1)当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。若不引入using namespace std ,需要这样做。std::cout。

2)  c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。

3)  C++命名空间的定义:   namespace name {  …  }

4)  using namespace NameSpaceA;

5)namespce定义可嵌套。

register关键字:

register关键字 请求编译器让变量a直接放在寄存器里面,速度快

在c语言中 register修饰的变量 不能取地址,但是在c++里面做了内容

1

register关键字的变化

register关键字请求“编译器”将局部变量存储于寄存器中

C语言中无法取得register变量地址

在C++中依然支持register关键字

C++编译器有自己的优化方式,不使用register也可能做优化

C++中可以取得register变量的地址

2

C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。

3

早期C语言编译器不会对代码进行优化,因此register变量是一个很好的补充。

函数检查增强全局变量的区别

.c中,重复定义,比如int t; int t = 0; 这样是不会报错的。但是C++中,是会报错 重命名。

struct类型加强

C语言的struct定义了一组变量的集合,C编译器并不认为这是一种新的类型

C++中的struct是一个新类型的定义声明

C++中所有的变量和函数都必须有类型

C++中对类型的检查更加严格,C++中所有的变量和函数都必须有类型,C语言中的默认类型在C++中是不合法的。

C++更加强调类型,任意的程序元素都必须显示指明类型

bool类型
bool类型只有两个值 0  1,占一个字节

三目运算

三目运算符是一个表达式,表达式不可能做左值。

int main()  
{  
    int a = 10;  
    int b = 20;  
    int c;  
    //三目运算符不可做左值  
    (a < b ? a : b) = 30;  
    //正确用法  
    c = (a < b ? a : b);  
    printf("c:%d", c);  
    system("pause");  
} 

const

const int *a; a指向的内存空间不能操作修改

int const *a; a的地址不能修改

在C语言中,const是一个冒牌货。const int a =10;a只是一个只读的量,可以通过指针绕过去修改。

C++中的const变量,可能分配内存空间,也可能不分配内存空间,分配内存空间的情况有:

              当const常量为全局变量,并且需要在其他文件中使用

              当使用&操作符取const变量的地址

引用

引用在实现上,只不过把间接赋值成立的三个条件的后两步合二为一

当实参传给形参引用的时候,只不过是编译器帮我们程序员手工取了一个实参地址,传给形参引用(常量指针)。



引用的强化讲解

我们先看一段代码:

#include "iostream"

using namespace std;

int myfunc1()
{
	int a; 
	a = 10;
	return a;
}
int& myfunc2()
{
	int a;
	a = 20;
	return a;
}

void main()
{
	int b1 = myfunc1();
	int b2 = myfunc2();
	int &b3= myfunc2();

	printf("b1:%d",b1);	//b1:10
	printf("b2:%d",b2);	//b2:20
	printf("b3:%d",b3);	//b3:乱码

	system("pause");
}

思考为什么b3是乱码?当执行26行代码的时候,编译器一看b3是一个引用,就会帮我们执行*p,但是a是一个局部变量,执行完就会销毁,所以打印时就会是乱码了。

但是为什么b2是正常呢?当21行代码执行的时候,b2是一个变量,已经接回myfunc2()的返回值,直接打印就可以了。

我们继续看下面的代码:

int& myfunc3()
{
	static int a;    //注意这里!!!
	a = 30;
	return a;
}

void main()
{
	int b1 = myfunc3();
	int b2 = myfunc3();
	int &b3= myfunc3();

	printf("b1:%d",b1);	//b1:30
	printf("b2:%d",b2);	//b2:30
	printf("b3:%d",b3);	//b3:30

	system("pause");
}
static在printf("b3:%d",b3);时,a的地址并没有被系统析构回收,因此这里b3的值就不会是乱码了。
好,我们再继续看下面一段代码:

int& myfunc4()
{
	static int a = 10;
	printf("%d\n", a);
	return a;
}

void main()
{
	myfunc4() = 11;
	myfunc4();

	system("pause");
}
先说结果,结果是10 11。为什么呢?

static int a =10这句代码就把a定义为一个静态变量,是一个状态变量,而且只初始化一次,即多次重复执行到这句话时,编译器会直接跳过,因此,当把a赋值成11以后,a的值就成11了。

继续深化,看两个函数的不同:

struct AdvTeacher
{
	char name[32];
	int age;
};

void getTeacher(AdvTeacher **p)
{
	AdvTeacher *tmp = (AdvTeacher *)malloc(sizeof(AdvTeacher));
	tmp->age = 20;
	*p = tmp;
}

void getTeacher01(AdvTeacher *&p)
{
	p = (AdvTeacher *)malloc(sizeof(AdvTeacher));
	p->age = 30;
}

void main()
{
	AdvTeacher *t1 = NULL;
	AdvTeacher *t2 = NULL;
	getTeacher(&t1);
	getTeacher01(t2);
	system("pause");
}
其实上面两个函数实现的功能是一样的。 

为了更好的说明引用的作用,我们来看下面的代码:

struct AdvTeacher
{
	char name[32];
	int age;
};


void getTeacher01(AdvTeacher *&p)
{
	p = (AdvTeacher *)malloc(sizeof(AdvTeacher));
	p->age = 30;
}

void getTeacher02(AdvTeacher p)
{
	p.age = 40;
}

void main()
{
	AdvTeacher *t1 = NULL;
	AdvTeacher  t2;

	getTeacher01(t1);
	getTeacher02(t2);

	system("pause");
}
其实
void getTeacher02(AdvTeacher p)<pre name="code" class="cpp">{
	p.age = 40;
}

getTeacher02(t2);
t2的值和p的操作是没有任何关系的,如果你已经掌握了指针的精髓,这里应该很好理解。


const的强化讲解

第一种:const引用的作用是:让变量所指向的内存空间为只读

struct AdvTeacher
{
    char name[32];
    int age;
};

void getTeacher(const AdvTeacher &p)
{
    //只能读,不能写。相当于const AdvTeacher * const t
    //p.age = 10;//err
    printf("age:%d\n", p.age);
}

int main(int argc, char const *argv[])
{
    AdvTeacher t;
    getTeacher(t);
    return 0;
}
第二种:给const引用初始化,有两种方法

int main(int argc, char const *argv[])
{

	//1.第一种初始化
	int a = 10;
	int &a1 = a;

	//继续
	int b = 20;
	const int &b1 = b;
	//b1 = 30;    //不能通过b1间接修改b
	b = 30;

	//2.第二种初始化
	//int &c = 10;//不可以
	const int &c = 10;
	return 0;
}

我们分析这句话

int &c = 10;
const int &c = 10;

为什么上面你的不可以? 因为第二句话,10 会在内存中临时分配内存空间,具体放在哪,需要由编译器决定。

const初始化有两种方法:

1. 用变量初始化,const引用分配内存空间了吗?

2. 用字面量初始化,const引用分配内存空间了吗?


内联函数

先看一下内联函数的声明和定义

#include "iostream"
using namespace std;

//内联函数的函数体,需要和实现写在一块
inline int myfunc(int a, int b)
{
	return a<b?a:b;
}

int main(int argc, char const *argv[])
{
	int a = 10;
	int b = 20;
	myfunc(a, b);

	system("pause");
	return 0;
}


C++编译器可以将一个函数进行内联编译

被C++编译器内联编译的函数叫做内联函数   废话

内联函数在最终生成的代码中是没有定义的。

C++编译器直接将函数体插入函数调用的地方

内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)

内联函数由编译器处理,直接将编译后的函数体插入调用的地方

宏代码片段由预处理器处理,进行简单的文本替换,没有任何编译过程。

C++内联编译的限制:

1.不能存在任何形式的循环语句

2.不能存在过多的条件判断语句

3.函数体不能过于庞大

4.不能对函数进行取址操作

5.函数内联声明必须在调用语句之前

结论:

1) 内联函数在编译时直接将函数体插入函数调用的地方

2) inline只是一种请求,编译器不一定允许这种请求

3) 内联函数省去了函数调用时的压栈,跳转和返回


学习一下内联函数和宏定义的代码:

#include "iostream"
using namespace std;

#define MYFUNC(a,b) ((a)<(b)?(a):(b))

inline int myfunc(int a, int b)
{
	return a<b?a:b;
}

int main(int argc, char const *argv[])
{
	int a = 10;
	int b = 20;
	int c;

	c= myfunc(++a, b);
	//a=11,b=20,c=11
	c= MYFUNC(++a, b);
	//((++a)<(b)?(++a):(b))
	system("pause");
	return 0;
}
仔细看上面的代码注释,切记!!!!


默认参数

1.C++可以在函数声明时为参数提供一个默认值,当函数调用时,没有指定这个参数的值,编译器会自动用默认值代替。

void myprint(int x = 1)
{
	printf("x:%d", x);
}

int main(int argc, char const *argv[])
{
	myprint(2);
	myprint();

	system("pause");
	return 0;
}
2.一旦在一个函数调用的形参中,开始使用默认值,那么这个默认值之后的变量必须使用默认值
void myprint1(int a, int x = 1, int y = 2)
{
	printf("x:%d", x);
}
//y必须有默认值
<pre name="code" class="cpp">void myprint2(int a, int x = 1, int y)
{
	printf("x:%d", x);
}

 

函数占位参数

函数占位参数只有参数类型声明,没有参数名声明,一般情况下,在函数体内内部无法使用占位参数。

int func(int a, int b, int)
{

	return a+b;
}
在调用上面的函数时,必须是三个变量,虽然第三个参量没有用到,但也必须给出三个形参,这叫占位参数。

默认参数和占位参数

int func(int a, int b, int = 0)
{
	return a+b;
}
在调用上面的函数时,可以不必是三个变量这叫默认参数和占位参数混搭。


函数重载

void myprint1(int a, int b)
{
	printf("a:%d b:%d\n", a, b);
}

void myprint2(int a)
{
	printf("a:%d\n", a);
}

void myprint3(char *p, int b)
{
	printf("p:%s b:%d\n", p, b);
}

void myprint3(int b, char *p)
{
	printf("b:%d p:%s\n", b, p);
}

int main(int argc, char const *argv[])
{
	myprint(1,2);
	myprint(1);

	return 0;
}
函数重载至少满足下面的一个条件:

1.参数个数不同

2. 参数类型不同

3. 参数顺序不同

函数返回值不是函数重载的必要条件。

函数重载遇上默认参数

void myprint(int a, int b)
{
	printf("a:%d,b:%d",a,b);
}

void myprint(int a, int b, int c =0)
{
	printf("a:%d,b:%d",a,b);
}

int main(int argc, char const *argv[])
{
	//对函数重载的不明,存在二义性
	myprint(1,2);
	
	myprint(1,2,3);

	return 0;
}
void myprint(int a, int b)
{
	printf("a:%d,b:%d",a,b);
}

int myprint(int a, int b)
{
	printf("a:%d,b:%d",a,b);
	return 0;
}

int main(int argc, char const *argv[])
{
	//对函数重载的不明
	myprint(1,2);

	return 0;
}
函数返回值类型不是函数重载的必要条件。


编译器调用重载函数的准则:

        将所有同名函数作为候选者

        尝试寻找可行的候选函数

        精确匹配实参

        通过默认参数能够匹配实参

        通过默认类型转换匹配实参


C++中函数重载主要用在类中

class cls
{
private:
	int m_a;
	int m_b;
public:
	void setfunc(int a)
	{
		printf("a:%d\n", a);
	}
	void setfunc(int a, int b)
	{
		printf("a:%d, b:%d\n", a, b);
	}
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值