1、作用域运算符
::解决归属问题(谁是谁的谁)
可以优先使用全局变量
2、命名空间
使用关键字namespace,控制标名称的作用域。
命名空间的本质:对符号常量、变量、函数、结构、枚举、类和对象等等进行封装
1、创建一个命名空间
#include <iostream>
using namespace std;
namespace A {
int data=10;
}
namespace B {
int data=20;
}
int main(){
cout<<"A::data"<<A::data<<endl;
cout<<"B::data"<<B::data<<endl;
return 0;
}
输出结果:
A::data10
B::data20
2、命名空间 只能定义在全局(重要)
3、命名空间可以嵌套
#include <iostream>
using namespace std;
namespace A {int data=10;namespace B {int data=20;
}
}int main(){cout<<"A::data"<<A::data<<endl;
cout<<"A::B::data"<<A::B::data<<endl;
return 0;
}
4、可以随时将新的成员加入命名空间
#include <iostream>
using namespace std;
namespace A {
int data=10;
}
//1000行代码后
namespace A {
void fun(void){cout<<"hello A"<<endl;
}
}int main(){cout<<"A::data"<<A::data<<endl;
A::fun();
return 0;
}
5、命名空间中函数的声明和实现分开
在命名空间内部声明,在外部实现
namespace B{
void fun1();
void fun2();
}void B::fun1(){cout<<"B中的fun1()"<<endl;}
void B::fun2(){cout<<"B中的fun2()"<<endl;
}
下面这种实现是错误的
namespace B{
void fun1();
void fun2();
}void fun1(){cout<<"B中的fun1()"<<endl;}
void fun2(){cout<<"B中的fun2()"<<endl;
}
6、无名命名空间
无名命名空间相当于static int a=10,只能在本文件下使用
namespace {
int a=10;
}int main(){
cout<<a<<endl;
return 0;
}
7、命名空间取别名
namespace B{
void fun1();
void fun2();
}
给B起别名为B2
namespace B2=B;
int main(){B2::fun1();
return 0;
}
8、使用using申明命名空间中的某几个成员可用
namespace A {
int data=10;
int a=100;
int b=200;
}
int main(){using A::a;//申明A命名空间中的acout<<"A命名空间中a="<<a<<endl;
return 0;
}
注:当在全局中使用时
using A::a;
int main(){int a=0;cout<<a<<endl;
return 0;
}
打印的是局部变量a=0
int main(){
using A::a;//可以看做局部变量
int a=0;cout<<a<<endl;
return 0;
}
程序报错
9、当using遇到函数重载
namespace A {
void fun1(){cout<<"fun1()"<<endl;
}
void fun1(int a){cout<<"fun1(int)"<<endl;
}
void fun1(int a,int b){cout<<"fun1(int,int)"<<endl;
}
}
int main(){
using A::fun1;//函数名即可
fun1();
fun1(1);
fun1(1,2);
return 0;
}
打印结果:
fun1()
fun1(int)
fun1(int,int)
10、using申明整个命名空间
using namespace std;
namespace A {
int a=100;
void fun1(){cout<<"fun1()"<<endl;
}
void fun1(int a){cout<<"fun1(int)"<<endl;
}
void fun1(int a,int b){cout<<"fun1(int,int)"<<endl;
}
}
int main(){
using namespace A;//加上 namespacecout<<a<<endl;
fun1();
fun1(1);
fun1(1,2);
return 0;
}
注:①当命名空间中与局部变量重名,先找局部变量
namespace A {
int a=100;
}
int main(){
using namespace A;//加上 namespaceint a=0;//当 命名空间中与局部变量重名,先找局部变量cout<<a<<endl;
return 0;
}
打印结果是0
②当命名空间中的变量与全局变量重名,程序报错
int a=0;
int main(){
using namespace A;//加上 namespacecout<<a<<endl;return 0;
}
error: reference to 'a' is ambiguouscout<<a<<endl;^
加上作用域解决
int a=0;
int main(){
using namespace A;//加上 namespacecout<<A::a<<endl;//使用的是A中的acout<<::a<<endl;//使用的是全局变量中的a
return 0;
}
所以当使用namespace时,不会和同名局部变量冲突,会和全局同名变量冲突
3、c++对c的增强
1、新增bool类型
2、结构体增强
结构体可以放函数,且给结构体赋值时可以省略struct
示例:
#include <iostream>
using namespace std;
struct student{int id;char name[30];void fun1(){cout<<"打游戏"<<endl;}
};
int main(){
student lucy={1,"lucy"};
cout<<"id:"<<lucy.id<<"name:"<<lucy.name<<endl;
lucy.fun1();return 0;
}
3、三目运算符增强
c++三目运算符返回值是引用,c语言返回的是值
int main(){
int a=10;
int b=20;
a>b?a:b=100;
cout<<"a="<<a<<"b="<<b<<endl;//三目运算符返回的是b=100,若是c中 则是10=100
return 0;
}
输出结果:a=10 b=100
4、c++的const 会对变量 优化
1、c++和c中的const都是修饰变量为只读。
2、c语言 严格准许 const修饰的是只读变量
但是在c语言中,可以通过指针对const的值进行修改
const int a=10;
int *p=(int *)&a;
*p=100;
3、c++的const 会对变量优化
1、如果以常量 初始化const修饰的变量 编译器会将变量的值 放入符号常量表中,不会立即给变量开辟空间
int main(){
const int a=10;
int *p=(int *)&a;
*p=100;
cout<<a<<endl;
return 0;
}
输出结果为10
2、只有当对a 取地址时 编译器才会给a开辟空间(只读变量)
int main(){
const int a=10;
int *p=(int *)&a;
*p=100;//只能修改*p所指向的值,并不能修改符号常量表中的值
cout<<a<<endl;//a=10
cout<<*p<<endl;//*p=100
return 0;
}
3、如果以变量 初始化const修饰的只读变量,没有符号常量表,立即开辟空间
int main(){int b=10;const int a=b;int *p=(int *)&a;*p=100;cout<<a<<endl;cout<<*p<<endl;
return 0;
}
输出结果:
100
100
4、如果以const修饰的是自定义类型的变量 也不会有符号常量表,立即开辟空间
#include <iostream>
#include <cstring>
using namespace std;struct STU{int num;char name[30];
};
int main(){const STU lucy={100,"lucy"};STU *p=(STU *)&lucy;p->num=20;strcpy(p->name,"lili");cout<<p->num<<endl;cout<<p->name<<endl;
return 0;
}
输出结果:
20
lili
5、c++中尽量使用 const代替define
①const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查
②const有作用域,而#define不重视作用域,宏不能作为命名空间、结构体、类的
成员,而const可以
5、引用
引用的本质:就是给变量名取个别名。
引用定义的步骤:
1 &别名
2 给哪个变量取别名 就定义该变量
3 从上往下整体替换
①给普通变量起别名
int main(int argc, char *argv[])
{int a=10;int &num=a;cout<<"a="<<a<<endl;cout<<"num="<<num<<endl;int b=20;num=b;cout<<"a="<<a<<endl;cout<<"num="<<num<<endl;return 0;
}
打印结果:
a=10
num=10
a=20
num=20
②给数组起别名
int main(int argc, char *argv[])
{
int nums[5]={1,2,3,4,5};
int length=sizeof(nums)/sizeof(nums[0]);
int (&arr)[5]=nums;
for(int i=0;i<length;i++){
cout<<arr[i]<<" ";
}return 0;
}
打印结果:
1 2 3 4 5
③给指针变量取别名
int main(int argc, char *argv[])
{int a=10;
int *p=&a;
int* &num=p;
cout<<"*p="<<*p<<endl;
cout<<"*num="<<*num<<endl;return 0;
}
打印结果:
*p=10
*num=10
④给函数取别名
void fun1(void ){cout<<"fun1()"<<endl;
}int main(int argc, char *argv[])
{
void (&myfun)(void)=fun1;
myfun();return 0;
}
⑤作为函数参数
void swap(int &x,int &y){//通过引用实现交换函数int temp=x;x=y;y=temp;}int main(int argc, char *argv[])
{
int a=10,b=20;
swap(a,b);
cout<<"a="<<a<<"b="<<b<<endl;return 0;
}
节约空间
eg:
struct STU{int age;char name[6];
};void fun1(STU stu){//占空间cout<<stu.name<<stu.age<<endl;
}
void fun2(STU &stu){//不占空间cout<<stu.name<<stu.age<<endl;
}
⑤引用作为函数返回值
int& fun3(){int num=100;return num;//返回num 外界就是给num取别名
}int main(int argc, char *argv[])
{
int &b=fun3();//由于num是局部变量在函数执行结束后会回收,所以打印结果为空白
cout<<b<<endl;return 0;
}
int& fun3(){static int num=100;//使用static延长生命周期return num;//返回num 外界就是给num取别名
}int main(int argc, char *argv[])
{
int &b=fun3();为空白
cout<<b<<endl;return 0;
}
链式操作:
struct stut{stut& print_stut(stut &s,int age){cout<<age<<endl;return s;}
};int main(int argc, char *argv[])
{stut s1;
s1.print_stut(s1,100).print_stut(s1,200).print_stut(s1,300);return 0;
}
输出结果:
100
200
300
⑥常引用
给常量去别名
void test10()
2 {
3 //int &a = 10;//err
4 const int &a = 10;//a就是10的别名
5 //a = 100;//err 不能通过常引用修改内容
6 cout<<a<<endl;
7 }
void fun4(int &arg){//为了节约空间使用了引用arg=200;//但是却有被修改的风险cout<<arg<<endl;
}
int main(int argc, char *argv[])
{
int num=100;
fun4(num);return 0;
}
通过常引用避免这种风险
void fun4(const int &arg){//为了节约空间使用了引用
// arg=200;errorcout<<arg<<endl;
}
⑦ 引用的本质
常量指针
int &b = a;//b为a的别名
int * const b = &a;
b = 100;//a的值为100 *b = 100;
6、内联函数
内联函数:在编译阶段 将内联函数中的函数体 替换函数调用处。避免函数调用时的开销。
内联函数 必须在定义的时候 使用关键字inline修饰, 不能在声明的时候使用inline
int my_add(int x, int y);//函数声明时,不要使用inline//内联函数 在定义的时候使用inline
inline int my_add(int x, int y)
{
return x+y;
}
1、宏函数和内联函数的区别(重要背)
1、宏函数和内联函数 都会在适当的位置 进行展开避免函数调用开销。
2、宏函数的参数没有类型,不能保证参数的完整性。而内联函数的参数有类型 能保证参数的完整性。
3、宏函数在预处理阶段展开,内联函数在编译阶段展开
4、宏函数没有作用域的限制,不能作为命名空间、结构体、类的成员
内联函数有作用域的限制,能作为命名空间、结构体、类的成员
2、内联函数的注意事项
在内联函数定义的时候加inline修饰
类中的成员函数 默认都是内联函数(不加inline 也是内联函数)
有时候 就算加上inline也不一定是内联函数(内联函数条件)
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数取地址
有时候不加inline修饰 也有可能是内联函数。内不内联 由编译器决定。
7、函数重载
函数重载的条件:
同一作用域,函数的参数类型、个数、顺序不同 都可以重载。(返回值类型不能作为重载的条件)
void printFun(int a)
{
cout<<"int"<<endl;
}void printFun(int a, char b){cout<<"int char"<<endl;}void printFun(char a, int b){cout<<"char int"<<endl;
}
void printFun(char a){cout<<"char"<<endl;}void test02(){printFun(10);printFun(10, 'a');printFun('a', 10);printFun('a');
}
8、缺省参数
在函数声明处 给函数参数一个默认的值,如果函数调用处,用户没用传实参,编译器就可以使用这个默认的值。
void sub(int a,int b=10){cout<<"a-b="<<a-b<<endl;}int main(int argc, char *argv[])
{
sub(20);//10
sub(30,20);//10return 0;
}
注意:如果函数的某个参数设置为默认参数, 那么这个参数的右边的所有参数 都必须是默认参数.
案例:一下默认参数正确的是
int func(int a, int b, int c=10);//正确
int func(int a, int b=20, int c);//错误 c必须默认参数
int func(int a=10, int b, int c);//错误 b c必须默认参数
int func(int a, int b=10, int c=20);//正确
int func(int a=10, int b, int c=20);//错误 b必须默认参数
int func(int a=10, int b=20, int c=20);//正确
缺省参数和函数重载同时出现 一定要注意二义性
void sub(int a){cout<<"a="<<a<<endl;
}void sub(int a,int b=10){cout<<"a-b="<<a-b<<endl;}int main(int argc, char *argv[])
{
sub(20);//上面两个函数都可以调用,出现二义性
return 0;
}
9、占位参数
//没有形参名的形参,叫占位参数void sub(int a,int ){cout<<"a="<<a<<endl;
}
int main(int argc, char *argv[])
{
sub(20,10);return 0;
}
占位参数 也可以是缺省参数(默认参数)
void sub(int a,int=10 ){cout<<"a="<<a<<endl;}int main(int argc, char *argv[])
{
sub(20,10);return 0;
}