C++ 对C的扩展
作用域运算符
符号
::
作用
1.当局部变量与全局变量重名时,区分局部变量与全局变量
变量名 局部变量
::变量名 全局变量
2.指明使用的变量所属的命名空间
命名空间名::变量名
3.实现函数的声明与定义分离
如:
namespace E{
void method();//函数的声明
}
void E::method02() //函数定义
{
}
如:C中代码
#include <stdio.h>int num = 10;int main(int argc, char const *argv[]){int num = 20;printf(" 局部变量 num = %d\n",num);// 在 c 语言中当局部变量与全局变量重名时 , 因为就近原则 , 默认使用的是局部变量// 此时无法使用全局变量return 0;}
如:C++代码
#include <iostream>using namespace std;int num = 10;int main(int argc, char const *argv[]){int num = 20;cout << " 局部变量 num = " << num << endl;cout << " 全局变量 num = " << ::num << endl;return 0;}
namespace与using
namespace
含义
命名空间
语法
namespace 空间名
{
//变量
//函数
}
注意
1.命名空间只能全局范围内定义(必须定义在函数外)
2.命名空间可嵌套命名空间
3.命名空间是开放的,即可以随时把新的成员加入已有的命名空间中
4.声明和实现可分离
5.无命名空间,意味着命名空间中的标识符只能在本文件内访问
6.命名空间别名
示例1
#include <iostream>using namespace std;namespace A{int a = 1;int b = 11;char c = 'A';}namespace B{int a = 2;int b = 22;char c = 'B';}// 命名空间可以嵌套namespace C{int a = 4;namespace D{int a = 5;}}// 命名空间是开放的,即可以随时把新的成员加入已有的命名空间中// 不能在次定义相同的变量namespace A{int d = 111;}namespace E{// 命名空间中可以定义函数void method01(){cout << "method01" << endl;}// 声明和实现可分离void method02();}void E::method02(){cout << "method02" << endl;}// 无名命名空间namespace{int a = 6;}int main(int argc, char const *argv[]){int a= 3;cout << "A a = " << A::a << endl;cout << "B a = " << B::a << endl;cout << "a = " << a << endl;cout << "C a = " << C::a << endl;cout << "D a = " << C::D::a << endl;cout << "A d = " << A::d << endl;cout << " 无名 a = " << ::a <<endl;E::method01();E::method02();return 0;}
示例2
#include <iostream>using namespace std;namespace A{int a = 1;}int main(int argc, char const *argv[]){// 命名空间别名namespace A1 = A;cout << "A a = " << A::a << endl;cout << "A1 a = " << A1::a << endl;return 0;}
using
作用
声明可使得指定的标识符可用
语法
1.声明命名空间中单个变量
using 命名空间名::变量名;
2.声明命名空间中单个函数
using 命名空间名::函数名;
3.声明整个命名空间
using namespace 命名空间名;
示例
#include <iostream>using namespace std;namespace A{int i = 10;char c = 'A';void methodA(){cout << "methodA" << endl;}}// 声明命名空间中的单个变量// 声明后在使用该变量就可以直接使用using A::i;// 声明命名空间中的单个函数// 声明后在使用该函数就可以直接使用using A::methodA;// 声明整个命名空间// 声明后整个命名空间中所以内容都可直接使用using namespace A;int main(int argc, char const *argv[]){cout << "A i = " << A::i << endl;cout << "A i = " << i << endl;methodA();cout << "A c = " << c << endl;return 0;}
注意
using遇到函数重载,using声明就声明了这个重载函数的所有集合重载:同一片空间中,函数名相同,形参列表不同,成为重载C语言不支持重载C++支持重载使用using声明或using编译指令会增加命名冲突的可能性
全局变量检测增强
如:C
#include <stdio.h>int a;//c 语言编译器会认为此处为变量的声明int a;int a = 10;// 变量的定义与初始化int main(int argc, char const *argv[]){return 0;}
如:C++
#include <iostream>using namespace std;int a;//c++ 编译器将此处理解为变量的定义//int a = 10;// 此时将会重复定义 , 程序无法编译通过int main(int argc, char const *argv[]){return 0;}
C++中所有的变量和函数都必须有类型
如:c
#include <stdio.h>void fun(i){printf("fun\n");}void fun02(){printf("fun02\n");}int main(int argc, char const *argv[]){fun(10);fun02();return 0;}
如:c++
#include <iostream>using namespace std;//c++ 中变量必须有类型 , 此时 i 没有类型所有无法编译通过// void fun(i)// {// }// 形参中的 void 表示没有形参列表void fun02(void){cout << "fun02\n";}int main(int argc, char const *argv[]){fun02();return 0;}
严格的类型转换
如:c编译通过
#include <stdio.h>#include <stdlib.h>int main(int argc, char const *argv[]){int *nums = calloc(10,sizeof(int));return 0;}
如:c++,必须强转
#include <iostream>#include <stdlib.h>using namespace std;int main(int argc, char const *argv[]){int *nums = (int *)calloc(10,sizeof(int));return 0;}
struct类型加强
c中定义结构体变量需要加上struct关键字,c++不需要
c中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,
也可以定义成员函数
示例
#include <iostream>using namespace std;struct Person{char name[50];int age;void eat(){cout << name << " 吃面条 " << endl;}void game();};void Person::game(){cout << name << " 玩游戏 " << endl;}int main(int argc, char const *argv[]){//c 语法struct Person p1 = {"tom",18};//c++ 语法 , 定义结构体变量时可以不用写 struct 关键字Person p2 = {"jek",18};p1.eat();p2.eat();p1.game();return 0;}
新增bool类型
关键字
bool 数据类型
true 真
false 假
true(转换为整数 1)和false(转换为整数 0)
bool类型的变量取值:true或false
bool 类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0)
注意
c 语言中也有 bool 类型,在 c99 标准之前是没有 bool 关键字, c99 标准已经有bool 类型,包含头文件 stdbool.h, 就可以使用和 c++ 一样的 bool 类型。
示例
#include <iostream>using namespace std;int main(int argc, char const *argv[]){cout << true + 0 << endl;cout << false + 0 << endl;bool tag = true;tag = false;tag = 10;cout << tag << endl;return 0;}
三目运算符功能增强
c 语言三目运算符返回的是值c++ 三目运算符返回的是变量
如:c
#include <stdio.h>int main(int argc, char const *argv[]){int a = 10;int b = 1;//c 语言中三目返回的是值 , 不是变量int c = a > b ? a : b;// 所以三目在 c 中不能作为左值 , 无法通过编译(a > b ? a : b) = 100;return 0;}
如:c++
#include <iostream>using namespace std;int main(int argc, char const *argv[]){int a = 10;int b = 1;//c++ 中三目返回的是变量 , 不是值int c = a > b ? a : b;// 所以三目在 c++ 中可以作为左值(a > b ? a : b) = 100;cout << "a = " << a << endl;return 0;}
c/c++中的const
全局变量
c
#include <stdio.h>const int num = 10;int main(int argc, char const *argv[]){//const 修饰的变量不能重新赋值//num = 1;// 无法进行修改 , 因为 num 的值存储在常量区int *p = #*p = 1;printf("num = %d\n",num);printf("*p = %d\n",*p);return 0;}// 出现段错误
c++
#include <iostream>using namespace std;const int num = 10;int main(int argc, char const *argv[]){//num = 1;int *p = (int *) #*p = 1;cout << "num = " << num << endl;cout << "*p = " << *p << endl;return 0;}// 出现段错误
#include <iostream>using namespace std;struct Person{int age;};const Person p = {2};int main(int argc, char const *argv[]){Person *p1 = (Person *)&p;p1->age = 10;cout << "p.age = " << p.age << endl;return 0;}
局部变量
c
#include <stdio.h>int main(int argc, char const *argv[]){const int num = 10;//const 修饰的变量不能重新赋值//num = 1;// 可以进行修改 , 因为 num 的值存储在栈区int *p = #*p = 1;// 修改完成后 num 的值也将被修改printf("num = %d\n",num);printf("*p = %d\n",*p);return 0;}
c++
#include <iostream>using namespace std;int main(int argc, char const *argv[]){// 在 c++ 中 const 修饰的变量会存储在常量表中const int num = 10;//num = 1;// 当我们对常量表的中的变量区地址时 , 系统会开辟一片内存 , 使其存储其变量的值int *p = (int *) #// 此时使用指针对其修改 , 修改的是新开辟的内存中的值*p = 1;// 此时 num 的值依旧从常量表中获取 , 值并没有发生改变cout << "num = " << num << endl;cout << "*p = " << *p << endl;return 0;}
#include <iostream>using namespace std;int main(int argc, char const *argv[]){int a = 10;// 在 c++ 中 const 修饰的变量以其他变量初始化 , 会为其分配内存空间const int num = a;//num = 1;int *p = (int *) #*p = 1;// 此时 num 的值依旧从常量表中获取 , 值发生改变cout << "num = " << num << endl;cout << "*p = " << *p << endl;return 0;}
#include <iostream>using namespace std;struct Person{int age;};int main(int argc, char const *argv[]){const Person p = {2};//const 修饰自定义类型的变量 , 无法修改其自定义变量中的成员变量//p.age = 10;// 无法获取其成员变量的地址//int *p = &(p.age);//const 修饰的自定义类型的变量 , 无法获取其地址 , 因为地址是使用 const 数据类型 * 修饰的// 转换后就可以使用该指针修改其自定义类型中的成员变量Person *p1 = (Person *)&p;p1->age = 10;cout << "p.age = " << p.age << endl;return 0;}
总结(重点)
全局变量:c/c++没区别,会出现段错误
局部变量:
1.如果const修饰的普通类型的变量,使用常量初始化如:const int a=10;此时会生成符号表,当获取其变量的地址时会开辟新的地址,使用获取的地址修改其值,不会影响符号常量表中的数据2.如果const修饰的普通类型的变量,使用变量初始化如:int x=10;const int a=x;此时不会生成符号常量表,会直接开辟地址,获取的地址修改其值,会修改其内容3.如果const修饰的是自定义变量1.无法直接修改其自定义类型中的成员变量2.无法获取自定义类型中的成员变量的地址3.获取其自定义类型变量的地址需强转,强制过会就可以使用改地址修改其成员变量的值
建议
尽量以 const 替换 #defineconst 与 #define 的区别1 . const 有类型,可进行编译器类型安全检查。 #define 无类型,不可进行类型检查 .2 . const 有作用域,而 #define 不重视作用域,默认定义处到文件结尾 . 如果定义在指定作用域下有效的常量,那么 #define 就不能用
引用(重要)
英文名:reference
符号:
&
作用
给已有变量起别名
语法
基本数据类型
数据类型 &变量名A=变量B;
给变量B取别名叫变量A
此时操作A就是在操作B,操作B就是在操作A
数据类型
方式1:
typedef 数组的数据类型 类型别名[数组长度]
类型别名 &别名=数组名;
方式2:
数组的数据类型(&别名)[数组长度]=数组名;
注意:此时数组长度不能忽略不写
指针
数据类型 *&别名=指针变量名;
常量
const 数据类型 &别名=变量名;
注意
1.不能返回局部变量的引用
2.函数当左值,必须返回引用。
本质
引用的本质在c++内部实现是一个指针常量
Type& ref = val; // Type* const ref = &val;
如
#include <iostream>using namespace std;struct Stu{int age;};// void test(Stu s)// {// s.age = 10;// }void test(Stu &s){s.age = 10;}int main(int argc, char const *argv[]){Stu stu = {1};test(stu);cout << "stu.age = " << stu.age << endl;return 0;}
内联函数
语法
inline 返回值类型 函数名 ( 形参列表 ){函数体}
特点
内联函数:在编译阶段像宏一样展开。作为类的成员函数 , 有作用域的限制内联函数为了继承宏函数的效率,没有函数调用时开销,然后又可以像普通函数那样,可以进行参数,返回值类型的安全检查,又可以作为成员函数。
注意
1,inline只能在定义函数的时候修饰。
2, 任何在类内部定义的函数自动成为内联函数。3,inline 修饰的函数是否为内联函数,取决于编译器。对于非 inline 修饰的函数,也有可能转成内联函数(体积小、功能简单的函数)。
条件
不能存在任何形式的循环语句不能存在过多的条件判断语句函数体不能过于庞大不能对函数进行取址操作
宏函数和内联函数区别(重要)
宏函数(带参宏)参数没有类型,不能保证参数的完整型。宏函数 在预处理阶段展开宏函数 没有作用域限制 不能作为类的成员内联函数:参数有类型 保证参数的完整性内联函数 在编译阶段 展开。内联函数 有作用域限制 能作为类的成员
对函数的加强
形参默认值
语法
返回值类型 函数名 ( 数据类型 变量名 1 = 值 , 数据类型 变量名 2 = 值 ,...){函数体}
注意
形参中有默认值的参数,在函数调用时可传可不传,如果不传使用默认值调用有默认值的函数,传入实参依据会按形参的顺序赋值如果某个形参有默认值,其后的形参必须也有默认值
形参占位符
如
void test(int a,int,int){}
注意
1, 形参占位符可以有多个2, 可以在形参的任意位置
重载
函数名相同,形象列表不同,称为重载
多态的一种体现