首先我们先回忆一下。
程序设计方法的发展历程
面向过程的结构化程序设计方法:
自顶向下、逐步求精
面向对象的方法:
封装、继承、多态
把对象的属性和操作封装起来,形成一个类;类是一个抽象的概念;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++中是不合法的。
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);
}
};