一、C++对C的加强
1、C++中所有变量和函数都必须有类型
C语言中函数的一些不好的使用方式:
(1)函数可以没有返回参数类型
(2)函数参数可以没有数据类型
(3)无参函数可以接收任意个参数
C++中的加强:
(1)int f()与int f(void)具有相同的意义,都表示返回值为int类型的无参函数,并且调用时不能给函数传参
(2)函数参数必须要有数据类型
2、新增bool类型关键字
(1)bool类型只有两种值,true为1,false为0
(2)0为假,非0为真,非0即是1
#include <stdio.h>
int main()
{
//bool类型只有两种值,true为1,false为0
//0为假,非0为真
//非0就是1
bool b = false;
int a = 0;
b = a - 1;//b = 1;
b++; //b = 1;
printf("b = %d\n",b);
return 0;
}
3、三目运算符的增强
(1)C语言中表达式返回的是一个值,C++中表达式返回的是变量本身
C++中(a > b ? a : b)与C语言中*(a > b ? &a : &b)效果相同
(2)三目运算符如果要作为左值,返回的表达式中必须没有常量
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
//C++中三目运算符可以作为左值
//但是不能出现常量
(a > b ? a : b) = 100;
printf("a = %d,b = %d\n",a,b);
return 0;
}
二、C++中的const
(1)C语言中const修饰的变量本质还是变量只是变成了只读属性,有自己的地址空间
(2)C++中const修饰的变量是一个真正的常量,没有地址空间,该常量的值被存放到常量表中
(3)当编译器检测到要对const修饰的常量进行取地址操作时,会为其分配内存但并不会被该常量使用
#include <stdio.h>
//可以用const代替宏常量
//#define是在预编译时进行处理的,const是在编译时进行处理的
//如果编译出错编译器并不能发现#define中具体是哪个变量出错只显示替换后的结果
//#define定义后一直往下都进行替换,直到用#undef取消定义
//const只在该{}内生效
int main()
{
//C++中const修饰的变量会作为常量将它的值存放到常量表中
//C语言中const修饰的变量仍然是变量只是变为只读变量
const int a = 10;
int *p = (int *)&a;
*p = 100;
printf("a = %d,*p = %d\n",a,*p);//a的值不变
return 0;
}
(4)可以用const来代替#define宏常量
(5)#define宏是在预编译的时候处理的,const是在编译的时候处理的
(6)宏的作用域从定义的那个位置开始,到文件结束都可见,除非使用#undef取消宏定义;const常量的作用域只在当前{}内
三、C++的引用
(1)普通引用在声明时必须用其他变量对其进行初始化
#include <stdio.h>
int main()
{
// 定义一个int型变量a,在内存中占4个字节,
// 变量名 a 就代表了这一块内存,或者说 a 是这块内存的别名
int a = 10;
// 定义了一个引用变量 b ,它是 a 的别名,和 a 代表同一块内存空间
// 对 b 的操作 和 对 a 的操作是一样的
int& b = a;
// 改变 b 等于改变了 a
b = 20;
printf ("%d, %d\n", a, b);
// a 与 b 代表同一块内存空间
printf ("%p, %p\n", &a, &b);
return 0;
}
(2)引用作为其它变量的别名而存在,因此在一些场合可以代替指针
#include <stdio.h>
void swap1 (int &a, int &b)
{
int tmp = a;
a = b;
b = tmp;
}
void swap2 (int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap2 (&a, &b);
swap1 (a, b);
printf ("%d, %d\n", a, b);
return 0;
}
(3)复杂数据类型传递
#include <stdio.h>
struct Student
{
char name[20];
int age;
};
void printS1(Student *pS)
{
printf ("%s\n", pS->name);
}
// 复杂数据类型的引用
void printS2(Student &pS)
{
(4)引用在C++中的内部实现是一个常指针,C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同
#include <stdio.h>
struct Student
{
char &a;
char &b;
};
int main()
{
printf ("%d\n", sizeof(Student));
return 0;
}
(5)函数返回值为引用
#include <stdio.h>
int &func()
{
static int a;
a++;
printf("a = %d\n",a);
return a;
}
int main()
{
//不能返回栈上的引用
for(int i = 0;i < 10;i++)
{
func();
}
//函数返回值为引用可以用引用去接
int &b = func();
b = 100;
func();
//用普通变量接接到的是该函数引用的值
int c = func();
c = 200;
func();
//函数返回值为引用可以将函数作为左值
func() = 200;
printf("b = %d\n",b);
return 0;
}
(6)常引用
int main()
{
// 常量 放在常量表中
const int a = 10;
int c = 10;
// 普通引用
int &b = c;
// 常引用,意思不能通过引用改变被引用的值
const int &d = c; // const int * const d = &c;
// 常引用的初始化有2中方式
// 1、引用普通变量,不能改变变量的值
{
int a1 = 100;
const int &ra = a1;
}
// 2、使用常量去初始化常引用
{
// 当使用常量对常引用进行初始化的时候,编译器会为这个常量分配一块空间
// 将这个常量的值复制到这个空间里
// 然后让这个常引用作为这个空间的别名
const int &ra = 10; // const int * const ra = &10;
// ra = 90;
int *p = (int *)&ra;
*p = 200;
printf ("ra = %d\n", ra);
}
return 0;
}
四、C++对C的函数扩展
1、内联函数
#include <stdio.h>
#define MAX(a, b) f((a) > (b) ? (a) : (b))
void f (int max)
{
printf ("max = %d\n", max);
}
// 内联函数代替宏函数
// 在普通函数前加 关键字 inline 将函数变成内联函数
// inline 必须要和函数定义放一起,如果和函数声明放一起会被忽略
// define 是预处理器处理的 inline 是编译器处理的
// define 没有函数参数检测,返回检测,作用域检测,inline 工作方式像一个函数
// 所以会有参数类型检测等
// inline是向请求在调用该函数的时候进行函数体的替换,编译器可能拒绝内联请求
// 如果内联成功,编译完以后代码是没有这一段函数的
// 内联函数函数体不能过长,最好不要超过 5 行
// 不能有循环语句,复杂的 if 语句
// 不能对内联函数进行取地址操作
inline int max(int a, int b)
{
return (a>b ? a : b);
}
int main()
{
int a = 10;
int b = 20;
f(10);
f (max(a, b++));
printf ("b = %d\n", b);
return 0;
}
2、默认参数
(1)函数的默认参数:当函数调用没有提供该参数的值,会使用默认的值
(2)如果给函数一个参数赋了一个默认的值,那么这个参数后面的所有参数都必须要有默认值
#include <stdio.h>
int add(int a, int b = 0, int c = 0)
{
return a+b+c;
}
int main()
{
printf ("%d\n", add(2));
printf ("%d\n", add(1,2));
printf ("%d\n", add(1,2,3));
return 0;
}
3、函数占位参数
(1)如果一个函数的参数只有类型,没有变量名,把这个参数叫做占位参数
(2)函数的占位参数可以和默认参数一起使用
#include <stdio.h>
int func2(int a, int b, int = 0)
{
return a + b;
}
int main()
{
//如果默认参数和占位参数在一起,都能调用起来
func2(1, 2);
func2(1, 2, 3);
return 0;
}
4、函数重载
(1) 函数重载:函数名相同,函数的参数不一样,根据调用的时候传的参数类型决定调用哪一个函数
(2)函数重载的规则:1、参数个数不同 2、参数类型不同 3、参数顺序不同
#include <stdio.h>
#include <string.h>
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
int main()
{
int c = 0;
c = func(1);
printf("c = %d\n", c);
c = func(1, 2);
printf("c = %d\n", c);
c = func("12345");
printf("c = %d\n", c);
return 0;
}
(3)对于引用,常量性不同可以作为重载的条件
int sub(int &a, int &b)
{
return a - b;
}
int sub(const int &a, const int &b)
{
return a - b;
}
int main()
{
sub(10, 20);
int a = 10;
int b = 20;
sub(a, 10);
return 0;
}
(4)当将一个重载函数赋给一个函数指针的时候严格匹配函数指针的类型#include <stdio.h>
#include <string.h>
int func(int x) // int(int a)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
typedef int(*PFUNC)(int a); // int(int a)
int main()
{
int c = 0;
PFUNC p = func;
c = p(1);
printf("c = %d\n", c);
return 0;
}