相关代码:cpp/month12/test_12/main.cpp · Hera_Yc/bit_C_学习 - 码云 - 开源中国
C++面向对象的语言
C++兼容C:因此,C++亦可以是面向过程的语言。
C++的基础知识是特别碎的,但这并不意味着这些知识并不重要,这些碎片化的内容是类和对象的基础,每一个知识后面都有很多的扩展,本文并不会讲解扩展内容,仅仅只是作为C++的基础来学习。不需要读者去深挖其中的含义。
一、命名空间
1、定义
定义命名空间,需要使用到namespace关键字。
namespace N1
{int a = 10;int b = 20;int func(int a, int b){return a + b;}
}
- 命名空间可以嵌套定义。
- 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
- 一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
2、使用
命名空间的使用有三种方式:
- 在变量前加命名空间名称和作用域变量符。
- 使用using引入命名空间的某个成员。
- 使用using namespace 命名空间名称 引入。
using namespace std;//C++库中所有的东西都是放在std命名空间里的namespace N1
{int a = 10;int b = 20;int func(int a, int b){return a + b;}
}using N1::b;
int main()
{printf("%d\n", N1::a);printf("%d\n", b);return 0;
}
关于命名空间std:C++库中所有的东西都是放在std命名空间里的。
二、输入输出
- 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。
- cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream >头文件中。
- <<是流插入运算符,>>是流提取运算符。
- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。
- 实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识,
//在日常练习中,不在乎和库命名冲突,可以直接用std命名空间
//在日后项目开发中:
//可以用std::cout,每次使用都要加std:: 用起来麻烦
//也可以把常用一些对象和类型展出来:
//工程中比较推荐这样
using std::cout;
using std::endl;
using std::cin;
//cout和cin
//与printf和scanf最大的区别:能够自动识别类型
int main()
{std::cout << "hello world!" << std::endl;int i = 1;double d = 1.11;std::cout << i << " " << d << std::endl;
}
三、缺省参数
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 0)
{cout << a << endl;
}int main()
{Func(10); //输出10Func(); //输出0return 0;
}
全缺省
//全缺省:
//有四种调用方式:(必须从左往右传入参数)
// 不传参数
// 只传入部分参数
// 传入全部参数
void Func(int a = 10, int b = 20, int c = 30)
{cout << "a=" << a << endl;cout << "b=" << b << endl;cout << "c=" << c << endl;
}
半缺省
//半缺省:(缺省部分参数)
//半缺省必须是从左往右缺省,不能间隔着给
void Func2(int a, int b = 20, int c = 30)
{cout << "a=" << a << endl;cout << "b=" << b << endl;cout << "c=" << c << endl;
}
缺省参数传参必须从左往右传参。
四、函数重载
1、函数重载
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数、类型、类型顺序)不同,常用来处理实现功能类似数据类型
不同的问题。
- 仅有返回值不同,不能构成重载。
//函数重载:函数参数顺序不同、参数类型不同、参数类型不同
int Add(int left, int right)
{return left + right;
}
double Add(double left, double right)
{return left + right;
}
long Add(long left, long right)
{return left + right;
}
2、重载的原因
3、以Linux下的C++函数名修饰规则为例(Windows下的规则过于复杂):
通过下面我们可以看出gcc(C语言编译器)的函数修饰后名字不变。而g++(C++编译器)的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。
(Windows的函数名修饰规则请读着自行查阅)
- gcc编译
- g++编译。
4、extern "C"
要求C和C++都能够使用C++的库(静态库/动态库)。
extern "C" void* tcmalioc(size_t n);
//extern "C"
//表示按C语言的修饰规则,到符号表找该函数
五、引用
1、引用
- 引用不是定义一个新的变量,而是给已存在变量取了一个别名。
- 编译器不会为引用变量开辟内存空间,它和它引用的变量公用一块内存空间。
2、性质
- 引用在定义时必须初始化。
- 一个变量可以有多个引用, 引用一旦引用一个实体,再不能引用其他实体。
3、常引用
“常”引用,即const修饰的引用。
关于指针的权限:
int main() {const int a = 10;// int& b=a;// 权限放大:const不能给非const,const只能给constconst int& b = a;int c = 20;// 权限缩小:非const能给非const,也可以给constint& d = c;const int& e = c;const int* pa = &a;// int* pa=&a; //编译不通过,属于权限的放大int* pc1 = &c;const int* pc2 = &c; // 编译通过,属于权限的缩小
}
4、引用和指针
- 在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
- 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
引用和指针的不同点:
- 引用是定义一个变量的别名,指针存储一个变量的地址。
- 引用在使用时必须初始化,指针没有要求。
- 引用在初始化一个实体后,不能在引用其他实体,而指针可以在任何时候指向任意一个同类型的实体。
- 没有NULL引用,但是有NULL指针。
- 在sizeof中含义不同:引用的结果是引用类型的大小,而指针始终是地址空间所占字节个数(32位平台x86占4个字节)。
- 引用自增即引用的实体增加1,指针自增即指针向后移动一个类型的大小。
- 有多级指针,但没有多级引用。
- 访问方式不同,指针需要显示解引用,引用编译器自动处理。
- 引用相比于指针更加安全。
5、使用场景
能用引用的场景,指针都可以使用,但是指针学习的成本比较高,因此许多面向对象的语言(Java等)不在使用指针。
(1)引用做参数
- 引用做参数可以作为输出型参数。
- 提高效率。
(2)引用做返回值
- 提高效率。
- 亦可以作为某一种参数,待补充……。
int Count1()
{static int n = 0;n++;return n;
}int& Count2()
{static int n = 0;n++;return n;
}
//static控制生命周期:局部变量出了作用域,不销毁(n没有了,但是存储的值还在)。
int main()
{const int& r1 = Count1();//int & r1=Count1(); //编译不通过,原因是:传值返回,返回的是这片空间的一个拷贝int& r2 = Count2();//编译通过,原因是:引用返回,返回的是n的别名,还是原来的那片空间//cout << n << endl;//编译不通过,n已经没有了,但是原来的空间还在。
}
为什么要用引用返回?
六、内联函数
1、 以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
内联函数适用于代码比较短,又被频繁调用的函数。
2、对于递归、代码特别长的函数,不适用于内联函数,inline只是对编译器的一个建议,因此当代码被inline修饰但不适用内敛时,编译器不会展开内联函数。(编译器有决定权)。
3、inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
inline void Swap(int& x1, int& x2)
{int tmp = x1;x1 = x2;x2 = tmp;
}
//频繁调用Swap,调用函数需要建立栈帧,是有消耗的
//如何解决:1、C语言使用宏函数 2、C++使用内联函数int main()
{int a = 0;int b = 1;Swap(a, b);return 0;
}
4、如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。
查看方式:
1. 在release模式下,查看编译器生成的汇编代码中是否存在call Add。
2. 在debug模式下,需要对编译器进行设置,否则不会展开。
内联函数和宏
宏的优点:
1、增强代码的复用性。
2、提高效率。
宏的缺点:
1、宏不能被调试(预编译截断被全部替换了)。
2、导致代码的可读性变差、可维护性变差。
3、没有类型安全的检查。
C++中哪些技术可以替换宏?
1、const 修饰对象替换宏定义。
2、inline 修饰函数替换宏函数。
七、关键字auto
1、auto
- 使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。
- 因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
int main()
{int a = 0;auto b = a;//b的类型是根据a推导出来的int& c = a;auto& d = a;auto* e = &a;auto f = &a;cout << typeid(a).name() << endl;cout << typeid(b).name() << endl;cout << typeid(c).name() << endl;cout << typeid(d).name() << endl;cout << typeid(e).name() << endl;cout << typeid(f).name() << endl;return 0;
}
2、注意
- auto不能作为函数参数。
- auto不能直接用来声明数组。
- auto常与范围for搭配使用。
八、范围for
int main()
{int array[] = {1,2,3,4,5};for (auto& i : array){//引用类型才能改变array元素的值i *= 2;}for (auto i : array){cout << i << endl;}
}
- for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
- 与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。
for循环迭代的范围必须是确定的:
- 对于数组而言,就是数组中第一个元素和最后一个元素的范围;
- 对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。
九、指针空值
指针空值:nullptr。nullptr==(void*) 0
- 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
- 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
- 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。