C++基础篇
输入输出流iostream
向流写入数据<<运算符
<<运算符接受两个运算对象,此运算符将给定的值写到给定的ostream对象中:
左侧:运算对象为ostream对象,如cout、cerr、clog
右侧:运算对象是要打印的值
输出结果:写入给定值的那个ostream对象,即此运算符返回其左侧的运算对象。
表达式等价于:(std::cout << "Enter two numbers:") << std::endl;
写入endl效果
添加一个换行符,然后结束当前行,并将与设备关联的缓冲区(buffer)中的内容刷到设备中显示。缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流。
从流读取数据>>运算符
从给定的istream读入数据,并存入给定的对象中:
左侧:运算对象为istream对象,如cin
右侧:运算对象是一个对象
输出结果:运算符返回其左侧的运算对象。
表达式等价于:(std::cin >> v1) >> v2;
作为条件处理
while(std::cin >> value)
其效果是检测流的状态。
True:流是有效的,即流未遇到错误,那么检测成功。
False:当遇到文件结束符(end-of-file),或遇到一个无效输入时(例如读入的值不是一个整数),istream对象的状态会变为无效。
变量
初始化与赋值
初始化不是赋值,其含义是创建变量是赋予其一个初始值,而赋值的含义是值替换。
注意:
long double id = 3.1415926536;
int a{id},b = {id}; //错误
int c(id),d = id; //正确
如果使用列表初始化且初始值存在丢失信息的风险,则编译器报错。
变量声明和定义的关系
声明:使得名字为程序所知。一个文件如果想要使用别的地方定义的名字则必须写声明。
定义:负责创建与名字关联的实体。申请了储存空间,赋予初始值。
作用:
如果想要在多个文件中使用同一变量,就必须将声明和定义分离。变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件则对其声明,同时不能有重复定义。
声明关键字extern
extern int i; //声明i
int j; //定义,默认初始值
extern double pi = 3.1416; //赋值则为定义
使用extern和包含头文件来引用函数有什么区别?
1、extern的引用方式比包含头文件要简洁,extern的使用方法很是直接,想引用哪个函数就用extern声明哪个函数。
2、会加速程序的编译(预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。
引用(reference)与指针(pointer)
引用 &
int ival = 1024;
int &refVal = ival;
含义:为对象起另外一个名字,引用类型引用另外一种类型。
解释:定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。
性质:
1、引用必须初始化:初始值对象绑定。
2、引用初始化完成,则不能修改:已经完成绑定,无法重新绑定。
3、引用即初始化对象起一个别名,不会额外开辟空间:引用不是一个对象,没有开辟空间。
指针 *
int ival = 1024;
int *p = &ival; //&获取地址
*p = 42; //*解引用
含义:指针存放的是某个对象的地址(32位系统通常是4字节,64位系统通常是8字节)。需要获取地址,可以使用取地址符&;需要获取对象,可以使用解引用符*
空指针(null pointer)与野指针
空指针:赋值为0或者NULL,没有指向任何对象。c++11最好使用nullptr。
野指针:指针变量指向非法的内存空间。
void*指针
特殊的指针类型,可以存放任意的对象地址。
限制:
一般拿它和别的指针比较,作为函数输入输出,或者赋值给另一个void*指针。不能直接操作void*指针所指的对象,因为不知道是什么类型,无法确定能做哪些操作。
引用与指针区别
1、指针无需一定初始化,可以为NULL;引用必须初始化,总会指向一个对象。
2、指针定义会开辟一个地址空间;引用不会额外开辟空间。
3、指针本身是个对象,允许赋值和拷贝,生命周期内可以指向不同对象;引用初始化完成后便绑定,无法更换绑定。
const限定符
定义一种变量,它的值无法被修改。编译器在编译的过程中把用到该变量的地方替换成对应的值。
const int bufSize = 512;
指向常量的指针(pointer to const)
内容:不能通过该指针改变对象的值。但没有规定那个对象的值不能通过其他途径改变。
const double pi = 3.14;
const double *cptr = π
double dval = 3.15;
cptr = &dval; //指针常量可以指向一个非常量对象
dval = 3.14; //cptr指向对象的值被改变了
常量指针(const pointer)
内容:指针是对象,把*放在const关键字之前,把指针定为一个常量。必须初始化,初始化完成后,则指针不能重新指向新的对象(即存放的那个地址值无法修改)。
int errNumb = 0;
int *const curErr = &errNumb;
constexpr和常量表达式(c++11)
内容:
1、常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。
2、constexpr主要用于那些值在编译时就能确定的场合。它用于强调并利用编译时常量的特性。const则只是表明上只读,运行时一样可以通过其他方式改变。
3、而constexpr函数的返回值也必须是常量并且在编译时必须可以计算出来。
constexpr int mf = 20;
constexpr int limit = mf +1;
constexpr int sz = size(); //size是一个constexpr函数
constexpr int sqr1(int a){
return a*a;
}
const int sqr2(int a){
return a*a;
}
int main()
{
array<int,sqr1(10)> mylist1; //可以
array<int,sqr2(10)> mylist2; //不可以
return 0;
}
constexpr和指针
内容:constexpr仅仅对指针有效,与指针所指的对象无关。constexpr会把所定义的对象置为顶层const。
int a = 10;
const int* p = nullptr; //p是一个指针常量,可以改指向
constexpr int* q = nullptr; //q是一个常量指针,不可改指向
p = &a;
//q = &a;//错误
*q = a;
mutable
用来修饰属性的,表示可变,被mutable修饰的属性,可以在常函数中修改,也可以由常对象修改
处理类型
别名
typedef int *pst;
using Int = int; //c++11
指针、常量和类型别名
注意:
typedef int *pst;
const pst cstr = nullptr;//cstr是指向int的常量指针,不可修改指向
//cstr = &a; //错误
*cstr = 10;
//不等于
const int *cstr = nullptr;
解释:这种理解是错误的。声明语句中用到pst时,其基本数据类型是指针。可是用int*重写了声明语句后,数据类型就变成了int,*成为了声明符的一部分。这样改写的结果是,const int成了基本数据类型。前后两种声明含义截然不同,前者声明了一个指向int的常量指针,改写后的形式则声明了一个指向const int的指针。
auto类型(c++11)
内容:auto让编译器通过初始值来推算变量的类型。一条声明语句只有一个基本类型。
auto i = 0,*p = &a; //int
decltype类型(c++11)
内容:有时候希望从表达式的类型推算出要定义的变量的类型,但是不想用其表达式的值初始化变量。而decltype的作用就是选择并返回操作数的数据类型。
注意:decltype((decl))双层括号的结果永远是引用类型。或者表达式内容是解引用操作,结果也是引用类型。
int decl = 50;
decltype(decl) a1 = 100; //int
decltype((decl)) a1 = decl; //int&
decltype(*p) a1 = decl; //int&
友元
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问,但是,有时候需要在类的外部访问类的私有成员,怎么办?
解决方法是使用友元函数,友元函数是一种特权函数, c++允许这个特权函数访问私有成员。
这一点从现实生活中也可以很好的理解:比如你的家,有客厅,有你的卧室,那么你的客厅是ublic的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。
持续更新中