目录
一、命名空间的基本常识
1、创建名字是程序设计过程中一项最基本的活动,当一个项目很大时,它会不可避免地包含大量名字。c++允许我们对名字的产生和名字的可见性进行控制。
之前子啊学习C语言的时候可以通过关键字static来使得名字值得在本编译单元中可见,也就是只在本文件使用,在C++中使用一一种命名空间来实现对名字的访问
2、在C++中名称可以是符号常量,变量、宏、函数、结构、枚举、类和对象等等。
3、std是c++标准命名空间,c++标准程序库中的所有标识符都被定义在std中,比如标准库中的类iostream、vector等都定义在该命名空间中,使用时要加上using声明(using namespace std) 或using指示(如std::cout)如下
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
又或者
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}
二、命名空间的使用
1、命名空间的定义
namespce //名称
{
//定义变量,函数,类型,对象
}
2、命名空间成员的访问
- 使用作用域操作符 “::”
- 空间名 ::成员
#include <iostream>
using namespace std;
namespace nameA{
int x;
void fun()
{
cout <<"nameA"<<endl;
}
}
namespace nameB{
int x;
void fun()
{
cout <<"nameB"<<endl;
}
}
int main() {
nameA::x=100;//给两个不同命名空间的变量赋值
nameB::x=1000;
//访问空间中的成员变量
//打印 此处打印与C语言也有区别
cout << nameA::x <<endl;//打印的话直接在里边cont << << endl里边放变量即可
cout << nameB::x <<endl;
//访问空间中的成员函数
nameA::fun();
nameB::fun();
return 0;
}
输出结果:
注意:命名空间只能全局范围内定义
3、命名空间的嵌套定义
//命名空间的嵌套定义
namespace nameC
{
int c=100;
namespace A
{
int c_a=200;
}
}
使用:
//命名空间的嵌套定义
cout << nameC::c << endl;
cout << nameC::A::c_a << endl;
4、命名空间的声明和实现分开
- 在test.h中声明命名空间
#ifndef F2_TEST_H #define F2_TEST_H //声明一个名字空间 namespace Myspace { //声明函数 void func1(); void func2(int x); } #endif //F2_TEST_H
- 在test.cpp中实现命名空间
#include "iostream" #include "test.h" using namespace std; //实现Myspace中的语句 void Myspace::func1() { cout << "void Myspace::func1()"<<endl; } void Myspace::func2(int x) { cout << "void Myspace::func2():"<<x<<endl; }
- 在main.cpp中使用命名空间
#include <iostream> #include "test.h" using namespace std; int main() { Myspace::func1(); Myspace::func2(100); return 0; }
打印结果:
5、给命名空间取别名
一般命名空间取名字的话,咱们都是根据他的英文来写,有的很长如下
//对命名空间的别名 namespace studentInfoHandle { int A=1000; }
如果想调用空间studentInfoHandle的A,需要输入很长一串,但我们可以通过下面的方式给一些命名空间取别名。
//给命名空间取别名 namespace SM = studentInfoHandle ; cout << SM::A << endl;
二、引用
概念:
1、引用可以看作一个已定义了的变量的别名
2、引用的语法:“Type & name = var;”
注意:
- &在此处不是一个取地址运算符,而是起表示作用
- 类型标识符是指目标变量的类型
- 必须在生命引用变量时经行初始化
- 引用初始化后不能改变
- 不能有NULL引用,必须确保引用时一块合法的储存单元关联
引用的使用:
#include "iostream" using numespace std; int main() { //引用到使用 int a= 10; //给变量a取一个别名 int & b = a; cout << "a:"<<a<<endl; cout << "b:"<<b<<endl; cout << "---------------------"<<endl; //操作b就相当于操作a本身 b=100; cout << "a:"<<a<<endl; cout << "b:"<<b<<endl; cout << "---------------------"<<endl; //一个变量可以有n个别名 int & c = a; c=200; cout << "a:"<<a<<endl; cout << "b:"<<b<<endl; cout << "c:"<<c<<endl; cout << "---------------------"<<endl; //a、b、c、的地址都是相同的 cout << "a:"<<&a<<endl; cout << "b:"<<&b<<endl; cout << "c:"<<&c<<endl; cout << "---------------------"<<endl; return 0; }
打印结果:
引用的本质:
1、引用的本质在c++中内部实现是一个常指针。
Type & ref = val;//Type* const ref = &val; //看不懂后边注释的建议回看一下 const 的用法
2、c++编译器在编译过程中使用常指针作为引用的内部实现,因此,引用占用的空间大小与指针相同,只是这个过程实在编译器中实现的用户不可见。
int & aRef = a;//自动转换为int* const aRef = &a;这也就说明引用为什么要进行初始化 eRef = 20;//内部发现aRef是引用,自动帮助我们转换为:*aRef = 20; cout << "a:"<< a << endl;
引用所占的内从空间大小:
#include <iostream> using namespace std; struct test { int& a; int& b;//此处引用没有进行初始化,是因为结构体中的变量不能进行初始化 }; int main{ //引用所占的内存空间大小: //指针变量在32位机器中占4个字节,指针变量在64位机器中占8个字节, //注意: float ch = 'a'; float & p = ch; cout << sizeof(p) << endl;//sizeof()关键字求的是某个数据类型所占用的内存空间大小//sizeof('a') cout << sizeof(97) << endl;//ascii表 cout << sizeof('a') << endl;//字符 cout << sizeof(struct test)<<endl;//用结构体来表示内存空间大小 return 0; }
sizeof()关键字是求某个函数的数据类型所占用的内存空间大小,但是在表示引用的类型的空间大小的时候,编译器内部会把引用转变为ascii类型,输出打印。进而如果我们想获取引用的内存空间的大小的时候,我们可以使用上述结构体来获取。
引用作为函数的参数:
注意:普通引用在声明时必须使用其他变量进行初始化,引用作为函数声明的时候不进行初始化,下边将用四个方式的代码与c语言的代码进行比较来表示其用法 。
#include <iostream> using namespace std; //引用作为函数的参数 typedef struct Teacher { char name[32]; int age=30; }Teacher; void printfT1(Teacher & t) { cout << "T1:"<<t.age<<endl; t.age=31; } void printfT2(Teacher t) { cout << "T2:"<<t.age<<endl; t.age=32; } void printfT3(Teacher* t) { cout << "T3:"<<t->age<<endl; t->age=33; } void printfT4(Teacher* const t) { cout << "T4:"<<t->age<<endl; t->age=34; } int main{ //引用作为函数的形参 Teacher teacher; printfT1(teacher); cout << "T1_ch:"<<teacher.age<<endl; printfT2(teacher); cout << "T2_ch:"<<teacher.age<<endl; printfT3(&teacher); cout << "T3_ch:"<<teacher.age<<endl; printfT4(&teacher); cout << "T4_ch:"<<teacher.age<<endl; return 0; }
引用实质就是用的运用的常指针,所用可以和指针一样去修改结构体变量的数值。
引用的意义和实际运用场景:
1、引用作为其他变量的别名而存在,因此在一些场合中可以代替指针
2、引用相对于指针来说具有更好的可读性和实用性
3、如果引用的对象是普通的数据类型的其实和指针差不多,引用在函数形参为对象的引用以及函数返回值为对象的引用上非常广泛。
三、内联函数
1、内联函数的引用
补充:系统在调用函数的时候生成一个函数栈空间,函数返回的时候栈自动销毁掉。
1、在C语言当中我们经常把一些简短的并且执行复杂的计算写成宏,而不是函数,这样做的理由是为了执行效率,宏可以避免函数调用时的开销,这些都是由预处理完成。
2、举一个宏代替函数可能出现问题的例子
#include <iostream> using namespace std; #define Add(x,y) x+y int main() { int rel1=Add(10,20); cout << "add1:" << rel1 <<endl; int rel2=Add(10,20)*10; cout << "add2:" << rel2 <<endl; return 0; }
打印结果如下:
add2的值不是我们想要的300,这就是因为在于处理的过程中是文本替换,所以rel2 = Add(10,20)*10;被展开后变成了ret1=10+20*10
3、为了保持预处理宏的效率又增加安全性,而且还能想一般成员函数那个样可以再 类(后边会介绍)中访问自如,C++引入内联函数(inline function)。
4、内联函数为了集成宏函数的效率,没有函数调用时开销,然后又可以像普通函数那个样,进行传参,返回值类型的安全检查,又可以作为成员变量函数。
2、内联函数的基本概念
1、在C++中预定义宏的概念使用内联函数来实现的,而**内联函数本身也是一个真正的函数** 。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开(此处为核心),所以不需要函数调用的开销(函数栈的创建和销毁)。因此应该不使用宏,使用内联函数
2、在普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
inline int func(int a){return ++a;}
3、内联函数的注意事项
1、C++中推荐使用内联函数替代宏代码片段
2、内联函数在最终生成的代码中是没有定义的,C++编译器直接将函数体插入在函数调用的地 方,内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)
3、因为内联函数在最终生成的代码中是没有定义的,所以内联函数的作用域可以理解为只在定义的文件内。假如在a.cpp中定义了inline int func(int a){return ++a;},如果在b.c中需要调用func函数则在b.cpp中需要重新定义内联函数inline int func(int a){return++a;}(也就是没法多文件调用)。
4、inline只是对编译器的一个内联请求,c++内联编译会有一些限制,以下情况编译器可能考虑不会将函数进行内联编译:
- 存在任何形式的循环语句
- 存在过多的条件判断语句
- 函数体过于庞大
- 对函数经行取地址操作
以上四种都会造成内联请求失败!!!
四、函数的默认参数
·1、c++在声明函数模型的默认参数的时候,可为一个或者多个参数指定默认(缺省)的参数值,当函数调用的时候如果没有指定这个值,编译器会自动默认值代替。
#include <iostream> using namespace std; //函数的默认参数//不同于C语言 int test (int a=10,int b=20) { cout << "a+b="<<a+b<<endl; return a+b; } int main() { //函数的默认值 test(); test(100); test(100,100); return 0; }
打印结果:
2、注意事项:
函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数也必须设置默认参数。
//这个函数形式是可以的 void test1(int x,int y=10){ cout << "x+y="<<x+y<<endl; } //但是下边这个函数形式却不可以 void test2(int y=10,int x) { cout << "x+y="<<x+y<<endl; }
如果函数声明和函数定义是分开写的,函数声明和函数定义不能同时设置默认参数,要在多文件中调用的话就要在声明函数的时候设置参数默认值
五、函数重载
1、概念
重载函数:用同一个函数名定义,不同的个函数,但函数名个不同参数搭配的搭配时候配置不含义.。
- 作用在同一个作用域,(在一个文件下,或者命名空间下),等等
- 参数的个数不同
- 参数的类型不同
- 参数的顺序不同
2、函数重载的实现原理
1、编译器在将我们的程序编译完成后会将变量和函数变成一个一个的符号,存放这些符号的表格我们叫符号表。
2、对程序进行编译查看函数对应的符号
用gcc编译器和g++编译器编译的效果是不一样的;
g++编译器在将函数转换成符号时,根据函数名、形参类型进行转化的
所以其实所谓的重载都是和编译器有关!
想要深入编程语言,还需要知道编译器的问题!!!