C++入门
第一个C++程序
首先C++兼容c语言,所以由c语言实现的内容仍然可以在C++中实现,但是c语言的文件后缀是.c但是C++的后缀是.cpp。vs对于cpp文件使用C++编译器编译,linux需要用g++编译而不是gcc
#include<stdio>
int main()
{
printf("hello world\n");
return 0;
}
C++的输出格式则为:
#include<isotream>
using namespace std;
int main()
{
cout<< "hello world" <<endl;
return 0;
}
其中包括的头文件 以及namespace都会在下文讲述
命名空间
namespace为命名空间
在c/c++中,有大量的变量、函数、类等因素,这些变量、函数体、类的名字都将存在于全局作用域中,很容易导致冲突,命名空间就是为了避免冲突,对标识符的名称进行本地化,以避免名空间的冲突,namespace就出现了。
例如:printf在类和全局中都定义了 这里会报错
#include <iostream>
using std::printf;
int printf = 1;
int main()
{
printf();
}
namespace定义
namespace定义命名空间,后面要跟命名空间的名字,然后使用{},在{}中写命名空间成员变量,其中成员可以是变量,函数。类型。
namespace my_std
{
struct ppprintf
{
};
class pppprintf
{
};
int pprintf()
{
}
int printf;
}
namespace是一个域,用于和其它域区分独立,使用不同的域可以取消一些命名冲突。
namespace my_std
{
int printf = 1;
}
#include<iostream>
using namespace std;
int main()
{
my_std::printf = 5;
printf("%d", my_std::printf);
return 0;
}
C++中有函数局部域,全局域,命名空间域,类域;域影响编译语时查找的变量/函数/类型的出处,或者声明定义的出处,隔离了域,就隔离了名字冲突。局部域和全局域会影响查找逻辑和变量的生命周期,命名空间域和类域不影响变量生命周期。
namespace只能定义在全局,也可以嵌套定义,不过也要嵌套调用。
多个工程文件(头文件,cpp文件等)中定义的同名域,会被认为成为同一个namespace,不会发生冲突。
//1.h
namespace a
{
namespace b
{
//...
}
}
namespace a
{
namespace c
{
//...
}
}
//2.h
namespace a
{
//...
}
//1.h和2.h的a被认为是一个域
//b 和 c都包含在a中
int main()
{
/*
a:://...
a::b:://...
*/
//嵌套调用
return 0;
}
C++标准库都放在std(standard)的命名空间中,使用using namespace std;可以展开std;
#include<isotream>
using namespace std;
int main()
{
cout<< "hello world" <<endl;
return 0;
}
命名空间的使用
编译查找一个变量的声明/定义的时候,默认在局部或者全局查找,不到命名空间中去查找,所以需要用以下方式使用。
指定那个命名空间访问
用using展开命名空间的某个成员
用using展开整个命名空间,不建议使用。
#cinlude<iostream>
using std::cout;//使用某个成员
using namesapce std;//展开整个命名空间
int main()
{
std::cout<<"hello world";//指明类域调用;
cout<<"hello world";//using std::cout后可以这样调用;
cout<<"hello world"<<endl;//endl是展开整个std中包含的;
}
C++的输入输出
<iostream>是标准输入。输出流库,定义了标准的输入输出对象。
std::cin是istream类的对象,它主要面向窄字符的标准输入流(narrow characters)。
std::cout是ostream类的对象,主要买那些窄字符的标准输出流。
std::endl是一个函数,流插入输出是,相当于插入一个换行字符和刷新缓冲区。
<<是流插⼊运算符,>>是流提取运算符。
C++的输入输出可以自己识别变量类型,更方便(使用函数重载实现的)。
IO流涉及到了类和对象,运算符重载、继承等面向对象的知识。
cout/cin/endl都属于C++的标准库,C++标准库都放在std的命名空间中,要通过命名空间的使用方式去使用
即使没包含<stdio.h>也可以使用printf和scanf是因为<iostream>中简介包含了。Vs系列包含了,其它编译器可能报错。
#include<iostream>
using namespace std;
int main()
{
int a = 1;char b = 'b';
cout<< a << ' ' << b << endl;
printf("%d,%c",a,b);
scnaf("%d,%c",&a,&b);
cin>>a;
cin>>b;
return 0;
}
缺省参数
缺省参数是声明或定义函数时作为函数的参数指定的缺省值,在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。
全缺省就是所有形参都给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数被絮从右往左依次连续缺省,不能间跳跃给缺省值。
函数声明和定义分离式,缺省参数不能再函数的声明和定义中同时出现,规定必须函数声明给缺省值。
//1.h
int add(int x;int y = 1)//从左往右给值 必须在声明中给缺省值
//1.c
int add(int x;int y)
{
return x+y;
}
int b(int x = 1;int y = 1)// 全缺省
{
return x-y;
}
函数重载
C++支持函数重载,要求同名函数的形参不同,或者参数个数不同,类型不同,形成了多态行为。
//参数类型不同
int a(int a,int b);
int a(char a,char b);
//参数个数不同
int a();
//参数顺序不同
int a(int a,char b);
int a(char a,int b);
//特殊
int a(int a = 1);
//这个会与 int a()冲突,编译器不知道调用谁,会报错。
引用
引用就是 类型&别名 = 引用对象
引用并不是定义了一个新的内存空间,就知识给已经存在的变量取了一个其它的名字罢了,并不会开辟新的内存空间,与原有的变量共用同一块内存空间。
#include<iostream>
using namespace std;
int main()
{
int a = 0;
int& b = a;//这里b就是a的别名 把b当a的类型操作就行了
++b;
cout<< b;
return 0;
}
引用的特性
引用在定义的时候必须初始化,因为是在已有的变量上取名字。
一个变量可以有多个引用
引用初始化后,就不能再引用到其它实体。
#include<iostream>
using namespace std;
int main()
{
int a = 0;
int& b = a;//如果是 int&b 会直接报错
//无法改变b的引用
return 0;
}
引用的使用
引用在实践中,主要用于传参和引用做返回值中减少拷贝提高小于和改变引用对象时同时改变引用对象。
引用传参和指针传参类似,引用传参相对方便。
引用做返回值也是存在的,之后会见到
引用和指针在实践中,虽然在功能上有重叠性,但是各有特点且互相不可替代。
引用可以简化程序,避开复杂指针
void swap(int& a,int&b)//引用版本
{
int c = a;
a = b;
b = c;
}
void swap(int* a,int*b)//指针版本
{
int c = *a;
*a = *b;
*b = *c;
}
const的引用
可以引用const对象,但是必须用const引用,const引用可以引用普通对象,因为相当于权限缩小了,权限只能缩小不能放大。
不过对于 int&b = a*3;doubel d = 1.1;int& c = d;的场景中,因为有计算,或者类型转换,会产生一个临时对象用于存储a*3的值和d类型转换的值,必须要用const,因为这个临时变量可以认为是const类型的。临时变量就是操作过程中的中间变量,只用来存值,没有名字。
int main()
{
int a = 1;
int& b = a;
const int& e = b;//e不能++ 权限缩小了
const int c = 1;
const int& d = c;
return 0;
}
指针和引用的关系
首先,他俩在C++中,相辅相成,虽然有功能重叠性,但是都有各自特点不可互相替代。
指针要开空间,变量只是取别名,不开辟空间。
引用必须初始化,指针可以不用初始化。
引用不可更改引用对象,指针可以更改指向对象。
引用大小就是引用类型大小,指针是对于平台下的大小。
指针会出现空指针也野指针的问题,引用很少出现,相对安全。
inline
inline修饰的函数,是内联函数,编译时C++会在调用的地方展开内联函数,这样调用内联函数就可以不建立栈帧了,可以提高效率。
inline对于编译器只是建议,展开与否由编译器决定,C++标准并没有规定这个。
inline适用于频繁调用的短小函数,对于递归和代码量很大的函数,即使加上了inline也会被编译器忽略。
C语言中,宏函数会在预处理时候展开,但是宏函数实现不仅复杂且容易出错,而且不方便调试,C++设计inline的目的就是替代宏函数。
inline不建议声明和定义分离,分离会导致连接错误,因为inline展开后就没有函数地址了,无法连接。
#include<iostream>
using namespace std;
//#defin ADD(x,y) ((x)+(y))
inline int ADD(int x,int y)
{
return x+y;
}
//不支持声明和定义分离到两个文件
int main()
{
int x = ADD(1,1);
return 0;
}
nullptr
NULL是一个宏定义
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
C++中NULL可能是字面意思的0,也可能是被定义为无类型指针常量((void*)0),无论采用何种定义,在使用空值的指针时候,都不可避免的会遇到一些麻烦,如f(NULL)会不确定的调用int*参数还是int类型参数。
C++11引用nullptr是一个关键字,是一种特殊类型的字面量,可以转换成任意其他类型的指针类型,使用nullptr可以避免类型转换问题,因为nullptr只能被隐式转换为指针类型不能转化为整数类型。