C++pimer第2章基础


#pic_center =400x
系列文章:



在main函数中,返回值0代表成功,非0值根据系统定义,指示错误类型。

不同的编译器使用后缀不同,如 .cc .cpp .cxx. .cp .C

标准库iostream 包含istream和ostream,定义4个对象:cin 标准输入, cout标准输出,cerr(see-err)用来输出警告和错误,cerr被称为标准错误, clog输出程序运行时的一般信息。
#include iostream是头文件, #include是指定
<<是运算符, 左侧必须是ostream对象,右侧是要打印的值,将给定的值写到给定的对象-ostream,运算符的结果是左侧对象。

endl 被称为操作符的特殊值,作用:1结束当前行,2将与设备关联的缓冲区中的内容刷到设备中。 缓冲刷新:可以保证到目前为止程序产生的所有输出都真正写入输出流,而不是停留在内存中等待写入流。实际场景:程序员经常在调试时写一些打印语句,就是在刷新流,让缓冲区中的内容打印出来,否则程序崩溃时,可能输出的内容还在缓冲区中。
std::cout , std::指出名字cout是定义在std命名空间中的,标准库中所有的名字都在命名空间std中。好处:避免自定义的名称以及标准库中同名冲突。繁琐:当使用标准库中的一个名字时,必须显示说明命名空间std中的名字, 即std::cout指出是std中的名字cout

C++注释:单行注释+界定符注释, 界定符注释/*内容*/,可用于单行多行,当多行是每行以一个星号开头指出整个范围都是多行注释的一部分。注释界定符不能嵌套

头文件引入:属于标准库中使用尖括号,不属于使用双引号

#include <iostram>
#include "Scales_item.h"

文件重定向:允许将标准输入和输出与命名文件关联起来
有的语言是运行时需要知道数据类型,而C++是编译时就进行类型检查

第二章变量和基本类型

2.1基本内置类型

算术类型

int, short, long, long long都是带符号, 不带符号直接加unsigned
字符分为char, signed char , unsigned char。 char根据编译器情况表现为signed和 unsigned
选择类型的建议:
不可能为负值:优选unsigned
超过int就选long long, 因为long一般和int有相同的尺寸
表达式尽量不使用char或bool,优选unsigned或者 singed char
浮点运算优选double
不要混用无符号和有符号,如有符号的负数和无符号计算出错
进制:10进制的整形字面量默认带符号,八进制和十六进制不确定

2.2变量

2.2.1变量定义

初始化方式分为列表初始化, 默认初始化。
列表初始化如下
初始化花样,花括号形式称为列表初始化,存在初始值丢失信息的风险且编译器会报错

int a = 0;
int b = {0};
int c{0};
int d(0)
long double ld = 3.1415926
int a{ld}, b={ld} ;  //编译报错,存在丢失信息风险
int c = ld, d(ld); //编译通过,丢失部分值

默认初始化

  • 默认初始化,变量赋予默认值。
  • 例外:定义在函数之外的内置类型会被初始化为0, 函数内部的内置类型不被初始化

变量声明和定义的关系

为了支持允许将程序拆分为多个逻辑部分写,c++支持分离式编译,每个文件独立编译, 多个文件存在引用关系时,即一个文件想要使用另一个文件的中某个名字,必须对改名字进行声明。
声明值规定变量的类型和名字; 而定义还申请了存储空间,有可能赋初值。 定义只能1次,声明可以多次。
函数体内部 , 由extern关键字标记的变量不能初始化

extern int i; // 声明i
int i; // 定义i
// 任何显示初始化的声明就成了定义, 这里的extern不起作用
extern double pi = 3.14 // 定义

静态类型: 编译阶段检查类型-类型检查

2.2.3标志符

2.3复合类型

如指针, 引用

2.3.1引用

分类: 左值引用, 右值引用(C++11新增)。
只说引用一般值左值引用。

  • 本质:引用就是给对象令起名字,引用符号$, 引用不是对象
  • 引用必须初始化
int ival = 1024;
int &refVal = ival; //refVal指向ival,是ival的另一个名字
int &refVal2; //报错,引用必须初始化
  • 定义引用后,对其进行的所有操作都是在与之绑定的对象上进行的。
    • 1、给引用赋值,实际把值赋值给引用绑定的对象。
    • 2、 获取引用的值,实际上是获取引用绑定的对象的值
    • 3、以引用为初始值,实际上是与引用绑定的对象作为初始值
refVal = 2; //把2赋值给refVal指向的对象,即赋值给ival
int ii == refVal; // 与ii = ival执行结果一样
int &refVal3 = refVal; //regVal3绑定到refVal绑定的对象-ival
int i = refVal ;// i初始化为ival
  • 引用不是对象,不能定义引用的引用
  • 引用的类型要与绑定的对象严格匹配
int &refVal4 = 10; // 引用的初始值必须是对象
double dval = 3.14;
int &refVal5 = dval; //错误,此处的引用类型的初始值必须是int型对象
  • 允许一条语句定义多个引用,每个引用标志符必须 以符号 &开头
int i2 = 1024, i3 =  2045;
int &r3 = i3, &r4 = i2;

2.3.2指针

指针与引用对比

  • 相同:都是对其他对象的间接访问
  • 不同:
    • 指针对对象, 允许对指针赋值和拷贝,在指针的生命周期内可以先后指向不同对象
    • 指针可以定义时不赋初值。指针没有初始化会有不确定的值

指针定义

  • 指针变量前必须有*符号
  • 指针存放某个对象的地址,获取对象地址使用取地址符&
  • 指针不能指向引用, 因为引用不是对象,没有地址
  • 指针的类型要与它指定对象严格匹配
int  ival = 42;
int *p = &ival;
double dval;
double *pd = &dval; //正确, pd指向double对象的指针
int *pi = pd; // 错误 pi类型与pd类型不匹配
pi = &dval; 错误 不能将double型对象地址赋值给int型指针

利用指针访问对象

  • 使用 解引用符\*来访问对象
  • 给解引用的结果赋值,实际是给指针指向的对象赋值
int ival = 42;
int *p  = &ival;
cout << *p; //得到的是p指向的对象,输出42
*p = 0; //p所指向的对象,给该对象赋值为0

符号的不同含义

int i = 42;
int &r = i; //引用
int  *p ; //指针
p = &i; //取地址符
*p = i; // 解引用符
int &r2 = *p; // 引用,解引用

空指针的声明

int *p1 = nullptr; //等价int *p1 = 0;  
int *p2 = 0; //p2初始化为字面常量0
int *p2 = NULL; // 等价int *p1 = 0;

最直接的方法 是nullptr,它是字面值,c++11新标准

赋值操作永远改变的是等号左侧的对象

pi = &ival; //pi值改变,指向ival
*pi = 0; //ival的值改变了, 指针pi没有改变

指针相同的情况:都为空指针; 指向同一个对象; 指向同对象的下一个地址; 一个指向某个对象,另一个指向另一个对象的下一个地址

void*指针
一种特殊类型的指针,可以存放任何对象的地址。
局限性:不能直接操作void*所指的对象,

2.3.3理解复合类型的声明

定义多个变量
变量的定义包括 一个基本都数据类型和一组声明符。同一条语句中,基本数据类型只有一个,但声明符形式不同,会定义出不同类似的变量。

// i是int型数, p是int型指针,r是int 型引用
int i = 1024, *p = &i, &r = i;

类型修饰符(*和&)只是声明符中的一部分,它的作用域并非本次定义的全部变量

int* p; // 合法,但容易误导
int* p1, p2; // p1是指针, p2是int
int *p1, *p2; //都为指针
int *p1;
int *p2;

指向指针的指针
一般来说声明符中的修饰符个数没有限制
如指针, **表示指向指针的指针, ***表示指向指针的指针的指针

int ival = 1024;
int *pi = &ival;
int **ppi = &pi;
cout<< ival<< *pi << **ppi

指向指针的引用
引用不是对象,没有地址,所以指针不能指向引用
指针是对象, 所以可以引用指针

int i = 42;
int *p;
int *&r = p; // r是一个队指针p的引用
r = &i; //r引用了指针p, 现在让p指向i
*r = 0; //解引用r得到i, 修改i的值为0

阅读tip: 从右到左阅读r的定义, 离变量名最近的符号(这里是&r的符号&)对变量有最直接影响, 因此r是引用。
声明符的其余部分用于确定r引用的类型是什么,此例中的*说明r引用是一个指针。声明的基本类型部分指出r引用是int型。
一条声明语句有 一个基本数据类型和紧随其后的一个声明符列表组成。
最简单int i = 0; 声明符就是变量名。
类型修饰符(* 或 &)只是声明符的一部分
int i = 1024, *p = &i, &r = i; p指针, r为引用

2.4 const 限定符

const是一种变量,必须要初始化(编译/运行初始化),且初始化之后不能改变值。

    const int i = s.length();//正确,运行时初始化
    const int j = 4; //正确,编译时初始化
    const int k; //错误,未初始化

默认状态下,const对象仅在文件内有效
多文件共享const对象,在变量定义前添加extern关键字
当编译初始化时:编译时会将const变量直接替换为常量
const int bufSize = 512; 会将所有用到bufSize的地方替换为512。在不同的文件中,为了避免对同一个变量的重复定义 ,const被设置为在当前文件有效。
需要多个文件共用const变量,不希望编译器为每个文件单独生成独立的变量,可以在声明或定义时添加extern关键字

// file_1.cc定义并初始化了一个常量,该常量能够被其他文件访问
extern const int bufSize = fcn();
// file_1.h头文件
extern const int bufSize // 与file_1.cc中的bufSize是同一个

2.4.1 const的引用

把引用绑定到const对象上,成为对 常量的引用

const int ci = 1024;
const int &r1 =  ci; //正确,引用及其对应的对象都是常量
r1 = 42; //错误,ci被const修饰,不能改变
int &r2 = ci; // 错误,不能让非常量引用指向常量对象

2.4.2指针和const PDF-83?

2.4.3顶层const?

指针本身是个对象,而指针又可以指向对象。
用名词顶层const表示指针本身是个常量
用名词底层const表示指针所指的对象是个常量。
更一般的,顶层const可以表示任意的对象是常量,如算数类型,类,指针等。
底层const需指针和引用等复合类型的基本类型部分有关。
比较特殊:指针类型即可以是顶层const又可以是底层const

2.4.4constexpr和常量表达式?

常量表达式:值不会改变且在编译过程就可以得到计算结果的表达式。
字面值是常量表达式。
用常量表达式初始化的const对象也是常量表达式
一个对象或表达式是不是常量表达式由它的数据类型和初始值共同决定

const int max_files = 20; //常量表达式
const int limit = max_files + 1 ; //是
int staff_szie = 27; //不是
const int sz = get_size(); //不是

constexpr变量
C++11新规定,允许将变量声明为constexpr类型,由编译器检测变量的值是否是一个常量表达式。
声明为constexpr的变量一定是一个常量,而且必须使用常量表达式来初始化

constexpr int mf = 20; //20是
constexpr int limit = mf + 1; //是
constexpr int sz = size(); //只有当size是一个constexpr函数时才是正确的声明语句

新标准允许定义一种特殊的constexpr函数,这种函数编译就可以得到计算结果,这样可以用constexpr函数初始化constexpr变量。
一般而言,变量是个常量表达式,就把它声明为constexpr

字面值类型
常量表达式的值需要在编译时得到计算,因此对声明constexpr时用到的类型必须有所限制,这些类型称为字面值类型
算术类型,引用,指针都属于字面值类型。
自定义类Scale_ite, IO库, string类型不属于字面值类型。
一个constexpr指针的初始值必须是nullptr或0,或存储在某个固定地址中的对象。
函数体定义的变量一般并未存在固定地址中,因此constexpr指针不能指向函数体内定义的变量。
定义在函数体之外的对象其地址固定不变,能用来初始化consteexpr指针。
允许函数定义一类有效范围超出函数本身的变量,这类变量和定义在函数体外的变量一样有固定地址.因此constexpr引用能绑定,constexpr指针能指向 。
指针和constexpr

2.5处理类型

类型别名是某种 类型的同义词,定义类型别名有两种方式,传统方法是关键词typedef, 新标准是 别名声明

typedef double wages; // wages是double的同义词
typedef wages base, *p; //base是double的同义词,p 是double*的同义词
using SI = Sales_item; //SI是Sales_item的同义词

指针、常量和类型别名

2.5.2 auto类型说明符

依靠编译器推断类型
auto一条语句声明多个变量,但基本数据类型必须一致

auto i = 0, *p = &i;
auto sz = 0, pi == 3.14;//错误,类型不一致

2.6.1 定义Scale_data类型

struct Scale_data {
  std:: string bookNo;
  unsigned units_sold = 0;
  double revenue = 0;
};
struct Scale_data {/* */} accum, trans *salesptr;
struct Scale_data {/* */} ;
 Scale_data accum, trans *salesptr; //建议使用这样的方式是

c++11 新标准:为数据成员提供一个类内初始值,没有初始值会默认初始化。

2.6.3 编写自己的头文件

类通常被定义在头文件中,而且类所在的头文件的名字应该和类的名字一样,如Sales_data类定义在Sales_data.h头文件中。
头文件通常包含那些只能被定义一次的实体如类、const和constexpr变量

预处理器
预处理器是编译前执行的一段程序,如预处理器看到#include标记的头文件就会用头文件的内容替换#include
预处理器应用:确保头文件包含多次仍然能安全工作
应用2:头文件保护符

猜你喜欢

转载自blog.csdn.net/weixin_42382758/article/details/127697672