【C++】C++入门
一、 C++关键字(C++98)
C++有63个关键字(C语言有32个),如下:
asm | do | if | return | try | continue |
auto | double | inline | short | typedef | for |
bool | dynamic_cast | int | signed | typeid | public |
break | else | long | sizeof | typename | throw |
case | enum | mutable | static | union | wchar_t |
catch | explicit | namespace | static_cast | unsigned | default |
char | export | new | struct | using | friend |
class | extern | operator | switch | virtual | register |
const | false | private | template | void | true |
const_cast | float | protected | this | volatile | while |
delete | goto | reinterpret_cast |
具体常用关键字的应用和功能在后续的学习过程中再进行介绍。
二、命名空间
在C/C++中,变量、函数和类都是大量存在的,如果这些变量、函数和类的名称将都存在于全局作用域中,可能会出现不同变量(或函数或类)的名字相同的命名冲突。使用命名空间可以避免命名冲突。不同命名空间里的命名相互独立,不可能发生冲突。
1.命名空间的定义
定义格式:namespace 命名空间的名字
{
//命名空间的成员,可以是内置类型变量、自定义类型定义(变量)、函数
}
例如:
namespace N
{
int a = 1;
int b = 2;
int Add(int x,int y)
{
return x+y;
}
}
2.命名空间的使用
2.1加命名空间名称及作用域限定符
即在需要使用命名空间的成员时,可以通过命名空间加限定符来访问。
例如:
int main()
{
printf("%d\n", N::a);//N是命名空间的名字,a是其内的成员
return 0;
}
2.2使用using将命名空间中某个成员引入
using可以用了来引入命名空间内的成员。
例如:
using N::b; //using引入命名空间的成员
int main()
{
printf("%d\n", N::a);//命名空间+限定符+成员名
printf("%d\n", b); //使用using引入后的命名空间的成员可以直接访问
return 0;
}
2.3使用using namespace 命名空间名称 引入
using namespace 加命名空间可以直接引入整个命名空间的成员。
例如:
using namespce N;
int main()
{
printf("%d\n", N::a);//N::a替换成a也可以正常运行
//命名空间引入后,其内的成员都可以直接访问
printf("%d\n", b);
Add(10, 20);
return 0;
}
三、 C++输入&输出
1 . 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件
以及按命名空间使用方法使用std。
即:
#include <iostream>
using namespace std;
注意:1.早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应
头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,
规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因
此推荐使用<iostream>+std的方式。
2.std是C++标准库的命名空间,如果工程较大,最好不要展开整个命名空间,防止命名冲 突,可以选择性的展开自己用到的成员。
2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream >头文件中。
3. <<是流插入运算符,>>是流提取运算符。
4. 使用C++输入输出更方便,可以自动识别变量类型。不需要像printf/scanf输入输出时那样,需要手动控制格式。需要格式化输出时,可以使用printf/scanf语句来进行输入输出(C++兼容C)。
5. cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载。
四、缺省参数
1.定义
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有传参,则采用该形参的缺省值,否则使用传参的值。
例如:
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的缺省值
Func(10); // 传参时,使用形参的值
return 0;
}
2.分类
缺省可以分为全缺省和半缺省,全缺省是指函数的参数列表的所有形参都有缺省,半缺省是指参数列表有部分参数没有缺省值。
全缺省举例:
void Func(int a = 10, int b = 20, int c = 30)
{
cout<<"a = "<< a <<endl;
cout<<"b = "<< b <<endl;
cout<<"c = "<< c <<endl;
}
半缺省举例:
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
注意:1.半缺省参数必须从右向左依次赋缺省值,(因为传参的时候都是从右向左一次传参);
2.缺省参数不能同时在函数声明和定义时出现,否则编译器会报错(重定义);
3.缺省值只能是常量或全局变量。
五、 函数重载
1.概念
函数名相同,函数参数的类型或个数不同的一组函数。其功能相似且在同一作用域内,常用来处理实现功能类似且数据类型不同的问题。
注意:如果只有函数返回类型不同,则不能构成函数重载。
例如:
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
return 0;
}
六、引用
1.定义
引用在语法上可以理解为给变量起别名,而不会开辟新的空间。
2.引用和指针的区分
1.引用必须初始化且不能改变指向;(引用只能是一个变量的别名)
2.指针可以不初始化且能改变指向。
3.引用的使用场景
1.输出型参数(即定义形参的类型,解决形参的改变不影响实参的问题)
2.做返回值(传值返回 vs 传引用返回)
传值返回:函数调用结束返回前,拷贝返回变量的值至临时变量,返回后销毁栈帧,将临时变量的值返回给主函数。(临时变量具有常性(const),其值不可修改)
传引用返回:函数返回类型是引用类型,返回返回值的引用(本质也是返回原变量,不过销毁栈帧前不会创建临时变量;所以更省时,但是必须在返回变量不在将要销毁的函数栈帧的情况下使用。(例如,static变量(储存在静态区))
对引用和指针进行赋值和初始化时,有权限的概念(相对变量本身而言)。权限可以缩小或保持,不能扩大。
因为const变量的值不可修改,故:
注意:“权限”只适用于利用引用和指针进行赋值和初始化时。
(const常变量的值初始化后不可修改,具有常性;临时变量具有常性,强转时也会产生临时变量作为中间变量,故:
第一种错误的原因在于,强转i为double类型时会产生一个临时变量,用于暂时存放将i转为double类型数据时的值,其不可修改,具有常性,故在利用引用进行赋值时,会扩大权限,故错误。
(const常变量必须初始化且不能修改)
七、内联函数
C++推荐:1.const 或enum替代宏常量;2.inline替代宏函数
原因:宏是直接替换,在预处理阶段就已经将宏常量和宏函数替换成对应的常量和常量表达式,不能进行调试。
宏的优缺点?
优点:
1.增强代码的复用性。
2.提高性能。
缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
内联函数:在编译阶段,编译器会将函数调用转变为函数展开,不会创建新的函数栈帧,达到了空间换时间的目的(即增长了代码但是减少了栈帧的开辟消耗的时间)。
也是因为这个原因,如果内联函数的函数体过长,函数展开会时目标文件过大,这个时候编译器可以自行决定是否对函数进行展开(内联函数只有建议展开的作用)。
劣势:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
注意:1.内联函数声明和定义不能分离,否则会报错。
2.内联函数不能出现递归。
3.类的成员函数如果在类内直接定义,则编译器默认其为内联函数。
八、 auto关键字(C++11)
功能:自动推导变量的类型。
例如:
int main()
{
int a = 5;
auto b = a;
auto c = &a;
return 0;
}
其中,b和c的类型分别被自动推到为int类型和int*类型。
但是上述例子中auto的使用显得不太必要,其实主要,auto运用于变量类型很长时用来定义变量,可简化代码,也减少书写带来的偶然错误。
auto*表示的变量必须是指针类型的变量,否则会报错;auto&表示的变量必须是某个变量的引用。
注:typeid(变量名).name()可以得到变量的类型。
注意:1.auto可以在一行定义多个变量,但多个变量的类型必须相同。
2. auto不能做形参;auto不能用来声明数组。
九、 基于范围的for循环(C++11) 语法糖
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因
此C++11中引入了基于范围的for循环。
for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
例如:
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array) //自动依次取数组中数据 取别名 e对象,自动判断结束
e *= 2; //修改数组元素的值,(如果for循环中不是引用而是auto,即是数
组元素的拷贝,则不能通过修改e的值来修改原数组元素的值)
for(auto e : array) //自动依次取数组中数据 赋值给 e对象,自动判断结束
cout << e << " ";
}
数组不存在传参的概念,传参时传过去的都是数组的首地址,形参是指针。
void TestFor(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}
上述函数是错误示例
array是指针,不允许使用基于范围的for循环。
十、指针空值---nullptr(C++11)
nullptr的类型是void*,是空指针,与C语言中的NULL含义相同。
但是C++中的NULL的类型是int,含义是0;NULL在C语言中表示空指针,由0强转而来,(void*)0
nullptr于C++而言就如NULL于C语言,但是NULL在C++中已不是空指针的含义。
总结
本文主要介绍了一些C++的入门知识,C++是C的进阶,兼容C但比C语言更完善。