C++ Primer 手记

  |   0 评论   |   0 浏览

1.数据类型

1.1 类型/字宽

  • 1.bool
  • 2.char 8bit
  • wchar_t 16bit
  • char16_t 16bit
  • char32_t 32bit
  • short 16bit
  • int 16bit
  • long 32bit
  • long long 64bit
  • float 6 位有效数字
  • double 10 位有效数字
  • long doubel 10 位有效数字
  • 可寻址最小内存块为 “字节” 8bit = 1byte
  • 存储的基本单元为“字” 1 word = 32/64bit = 4/8bytes
  • float = 1 word = 32bit = 4bytes
  • double = 2 word = 64bit = 8bytes
  • long double = 3word - 4word = 96-128 bit = 12 - 16 bytes
  • signed +/-/0
  • unsigned +/0
  • int,short,long,long long -> singed
  • 8bit unsigned char 00000000 - 11111111 : 0-255
  • 8bit signed char 1111 1111 - 0111 1111: -127-127

1.2 类型转换

  • bool b = 0 /false
  • bool b = !0 的数值 /true
  • int i = 1; i= 3.14; /i 的值为 3
  • i = 3.14;double pi = i; /pi 的值为 3.00
  • unsigned char c = -1; /c 的值为 255
  • signed char c2 = 256;/ 未定义的值

1.3 字面量

  • 20 十进制,024 八进制,0x14 十六进制
  • -42 的字面值是 42,-为取负运算,不计入存储
  • 'a'为 char 的字面值,"asdf"为'a','s','d','f',' \0'(空字符)组成的数组
  • 不可打印\n\t\a\v\b\?\r\f 转译序列标志为,转译序列被当作一个字符
  • \1234 = \123 4 两个字符
  • \x1234 = 一个字符
  • L'a' 为宽字符,wchar_t
  • u8"hi!" 为 utf-8 字面值
  • 42ULL = unsigned long long
  • 1E-3F float
  • 3.14159L long double
  • nullptr 为指针字面量

字符前缀

  • u unicode 16 字符 char16_t
  • U unicode 32 字符 char32_t
  • L 宽字符 wchar_t
  • u8 utf-8 char

整形后缀

  • u or U unsigned
  • l or L long
  • ll or LL long long

浮点后缀

  • f or F float
  • l or L long double

1.4 变量

  • 要素,类型说明符,初始值,初始化,列表初始化,默认初始化,不被初始化
  • int a=0;int a={0};int a{0};int a(0);
  • long double ld = 3.14;int c(ld),d = ld;

1.5 声明/定义

  • int i 声明
  • int i = 1 定义

statically typed 静态类型语言: 编译阶段检查类型,检查过程为 type checking

1.6 作用域

  • 函数外 全局
  • extern 外部
  • 函数内 块儿作用域
  • 嵌套 内外层作用域

1.7 复合类型

基本数据类型 + 声明符

1.8 引用类型

  • lvalue reference
  • rvalue reference
  • int ival = 1024; int &refVal = ival; 将 refVal 指向 ival

1.9 指针类型

  • 元素的地址
  • int ival = 42; int *p = &ival; 将 p 指向变量 ival
  • 四种: 指向对象,指向紧邻对象下一个位置,空指针,无效指针
int ival = 42;
int *p = &ival;
cout << *p;

指向指针的指针

int ival = 1024;
int *pi = &ival;
// 指向指针的指针,*以此类推
int **p = *p

指向指针的引用

int i= 42;
int *p;
int *&r = p;

r = &i;
*r = 0;

1.10 限定符

  • const
  • const int ci = 1024; const int &r1 = ci; 正确。 int &r2 = ci 错误,非常量引用指向常量
  • const pointer 必须初始化,无法改变指向地址
  • top-level const : int *const p1 = &i; const int p1;
  • low-level const: const int *p2 = &i;
  • top and low : const int *const p1 = &i;

一个对象/表达式是不是常量表达式由他的数据类型和初始值决定

const int max_files = 20; max_files为常量表达式
const int limit = maxfiles + 1 ; limit是常量表达式
int staff_size =27; staff_size 不是常量表达式
const int sz = get_size(): sz不是常量表达式

constexpr 由编译器检测是不是常量表达式

constexpr int mf = 20; 
constexpr int limit = mf+1; 
constexpr int sz = size(); size为constexpr函数时才是正确的声明语句

1.11 类型别名

  • typedef double ahaha; ahaha a = 3.14;
  • using ahaha = obj;
  • typedef char *pstring;pstring 为 char *

1.12 auto 自己分析吧

  • auto item = val+val1
  • auto 忽略顶层 const,需要显示声明 const auto f =ci;

1.13 decltype 推断数据类型

  • decltype(f()) sum = x;
  • const int ci = 0; decltype(ci) a = 1; a 类型为 const int;
  • int i=0;decltype(i) 为 int; decltype((i))为 int&

1.14 结构体

struct Sales_data(
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
); 不要忘记加分号

2.字符串,向量,数组

2.1 using

using namespace::name

直接访问命名空间的属性/函数/值?

#include <iostream>

using std::cin;
using std::out;

int main(){
	int i;
	cin >> i;
	cout << i;
	return 0;
}

2.2 string

#include <string>
using std::string
  • string s1;
  • string s2 = s1;
  • string s3 = "hiya";
  • string s4(10,"c"); "cccccccccc"

直接初始化和拷贝初始化

  • = 为拷贝初始化
  • != 为直接初始化
  • string s5 = "hiya" 拷贝初始化
  • string s6("hiya") 直接初始化
  • string s7(10,"c")直接初始化

string 的操作

  • os << s 写入流
  • is >> s 从流读出
  • getline(is,s) 从 is 流获取一行
  • s.empty() 判空
  • s.size() 长度
  • s[n] 第 n 个 char 的引用
  • s1+s2 连接
  • s1 = s2 常规赋值
  • s1 == s2 比较是否一样
  • 大于小于大于等于小于等于,字典顺序比较字符

读写 string

int main(){
	string s;
	// 读
	cin >> s;
	// 写
	cout << s << endl;
	return 0;
}
string s1,s2;
// 第一个输入扔到s1
// 第二个输入扔到s2
cin >> s1 >> s2;
// 输出两个字符串
cout << s1 << s2 << endl;
string word;
// 无限读,读到文件末尾
while(cin >> word){

};
-------
string line;
// 一次读一行
while(getline(cin,line)){
};

string::size_type

  • size 返回的是 string::size_type 类型的值,无符号类型的值

比较 string 对象

  • >/>=
  • </<=
  • ==
  • !=

处理 string 中的字符

#include <cctype>

int main(){
  // 是否为标点符号
  ispunct()
  // 是否为数字/字母
  isalnum()
  // 是否为字母
  isalpha()
  // 是否为控制字符
  iscntrl()
  // 是否为数字
  isdigit()
  // 不是空格但可打印
  isgraph()
  // 是否为小写字母
  islower() 
  // 是否为可打印字符
  isprint()
  // 空白
  isspace()
  // 是否为大写字母
  isupper()
  // 十六进制
  isxdigit()
  // 大转小,小转大
  tolower()
  toupper()

  return 0;
}

处理字符串

for(declaration: expression){
statement;
}

string str("some string");
// 循环字符串
for(char c:str){
  cout << c << endl;
}
// 统计标点符号
string s("Hello World!!!");
decltype(s.size()) punct_count = 0;
for(char c:s){
  if(ispunct(c)){
    punct_count++;
  }
}
cout << punct_count << endl;

改变字符串

string s("asdf");
// 这里c为s中每个char的引用,所以改变c会影响s
for(char &c:s){
  c = touuper(s);
}

下标访问

  • string s("asdf"); s[0] == 'a',s[s.size()-1] = 'f';

比较另类的迭代方式

// 声明index,index不为最后一位,且index不为空白字符
for(decltype(s.size()) index =0;index != s.size() && !isspace(s[index]);++index){
  ...
}

2.3 vector

容器

#include <vector>
using std::vector;

c++ 既有 class template 也有 function template,vector 属于 class template

  • vector ivec;
  • vector asdf;
  • vector zxcv;
  • vector exp(value)
  • vector exp = {v1,v2,v3}
  • vector exp(n,val)
  • vector 元素类型为内置类型,比如 int,自动初始化为 0
  • 如果 vector 对象中的元素类型不支持默认初始化,必须提供初始值
  • push_back()末尾添加
  • empty()判空
  • size()元素个数,不是长度
vector<int> a = {1,2,3,4,5,6}
for(int &i:a){
  a = a*a;
}
for(auto i:a){
  // 1,4,9,16,25,36
  cout << i << endl;
}

demo:读入一组整数,存入,将每对相邻的整数的和输出

vector<int> numbers;
int currentNumber;
while(cin >> currentNumber){
  if(isdigit(currentNumber)){
    numbers.push_back(currentNumber);
  }
}

for(decltype(numbers.size()) index = 0;index < numbers.size();++index){
 if(index == 0) {
  cout << numbers[index] + numbers[index+1] << endl;
 } else if(index == numbers.size() - 1){
  cout << numbers[index] + numbers[index - 1] << endl;
 } else{
  cout << numbers[index] + numbers[index - 1] + numbers[index + 1] << endl;
 }
}

demo2 第一个元素和最后一个元素的和,第二个元素和倒数第二个元素的和
粗略写一下

vector<int> numbers;
int currentNumber;
while(cin >> currentNumber){
  if(isdigit(currentNumber)){
    numbers.push_back(currentNumber);
  }
}

if(index%2==0){
// 0-5, 1-4, 2-3
cout << numbers[index] + numbers[numbers.size() - 1 - index] << endl;
}

if(index%2==1){
  numbers[index+1] == numbers[numbers.size() - 1 - index] break
  cout << numbers[index] + numbers[numbers.size() - 1 - index] << endl;

}

2.4 迭代器

  • *iter 迭代器元素的引用
  • iter->mem 解引用 iter 并获取元素的名为 mem 的成员,等价于(*iter).mem
  • ++iter 下一个元素
  • --iter 上一个元素
---
string s("some string");
if(s.begin() != s.end()){
  auto it = s.begin();
  *it = toupper(*it);
}
---
for( auto it = s.begin();it != s.end() && !isspace(*it);++it){...}

范型编程

  • vector::iterator it; it 只能读写 vector元素
  • string::iterator it2; it 只能读写 string 对象中的字符
  • vector::const_iterator it3; it3 只能读 int 元素不能写元素
  • string::const_iterator it4;只能读字符
const vector<int> cv;
auto it1 = v.begin(); it1 类型为 vector<int>::iterator
auto it2 = cv.begin(): it2类型为 vector<int>::const_iterator
// cbegin,cend-> const beigin,const end
auto it3 = v.cbegin(); it3类型为 vector<int>::const_iterator

结合解引用

  • (*it).empty()
  • it->empty() 等价于 (*it).empty()
for( auto it = text.cbegin();it != text.cend() && !it->empty();it++){
  cout << *it <<endl;
}

运算

  • iter1 - iter2 ; 两个迭代指示的距离,类型为 difference_type 的带符号整形
  • iter1 + n ; 移动 n 个位置
  • iter1 += n; iter1 +n 的结果赋值给 iter1
  • iter1.begin() + xxx.size()/2 ; 指示到中间位置

二分搜索

// 开始结束
auto beg = text.begin(), end = text.end();
// 中间点
auto mid = text.begin() + (end - beg)/2;
// sought为需要找的数
while(mid != end && *mid != sought){
  // 前半部分?
  if(sought < *mid){
    // 忽略后半部分
    end = mid;
  }else{
    // 忽略前半部分
    beg = mid +1;
  }
  // 拿到新的中间点
  mid = beg +(end-beg)/2;
}

2.5 数组

  • int a[10] = {xxxxxxx}
  • string a1[3] = {"asdf","asd","as"}
  • int a2[] = {}
  • char c[] = {'','\0'}
  • 不允许拷贝值
  • int *ptrs[10] 10 个指针的数组
  • int (*Parray)[10] = &array; Parray 指向一个含有 10 个整数的数组
  • int (&arrRef)[10] = arr; arrRef 引用一个含有 10 个元素的数组 arr
  • int *(&arry)[10] = ptrs; arry 是数组的引用,该数组含有 10 个指针,arry 是一个含有 10 个 int 型指针的数组的引用

访问数组元素

  • 使用数组下标时,定义为 size_t 类型,与及其相关的无符号类型
----
unsigned scores[11] ={};
unsgined  grade;
while(cin >> grade){
  if(grade <= 100){
    // 将当前分数段的计算数值+1
    ++scores[grade/10];
  }
}
---
for(auto i:scores){
  cout << i << endl;
}

指针和数组

string nums[] = {"one","two","three"};
// "one"
string *p = &nums[0];
// "two"
*p+1
---
int ia[] = {0,1,2,4,5}
auto ia2(ia); // ia2为整形指针,指向ia第一个元素
---
int arr[] = {0,1,2,3}
int *p = arr;
// 指针也是迭代器
++p;

标准库 begin,end

int ia[] = {0,1,2,3,4};
// 首元素指针
int *beg = begin(ia);
// 尾元素指针
int *last = end(ia);

---
int *pbeg = begin(arr),*pend=end(arr);
// 迭代demo
while(pbeg != pend && pbeg >0){
  ++pbeg;
}

指针运算

int arr[] = {1,2,3,4,....};
int *p = &arr[0];

  • p + 1 等价 &arr[1];
  • *(p+1) 等价 arr[1];
  • *(p+1)+1 等价 arr[1] + 1;

c 标准库 string 函数

  • strlen(p) length
  • strcmp(p1,p2) compare
  • strcat(p1,p2) concat
  • strpy(p1,p2) copy

与旧代码的接口

  • string s("hello world"); char *str = s; const char *str = s.c_str(); c_str 为 c 风格字符串

2.6 多维数组

// 3行4列
int ia[3][4] = {
  {1,1,1,1},
  {1,1,1,1},
  {1,1,1,1},
};
// 初始化为0
int ia[3][4] ={0};
// 初始化行首
int ia[3][4] ={{1},{2},{3}};
// 初始化第一行
int ia[3][4] = {1,2,3,4}
// 多循环处理多维数据
size_t cnt = 0;
for(auto &row:ia){
  for(auto &col:row){
    cout << ia[row][col] << endl;
  }
}

指针和多维数组

int ia[3][4];
int (*p)[4]  = ia; p指向有4个整数的数组
p = &ia[2]; p指向第二行的四个元素
// p 指向ia的首行4个元素
for(auto p=ia;p!=ia+4;++p){
  //  *p+4为该行第4个元素,q指向一行,也就是指向了p这一行的第一列元素
  for(auto q = *p;q!=*p+4;++q){
    cout << q << endl; 
  }
}
---
for(auto p = begin(ia);p!= end(ia);++p){
  for(auto q = begin(*p);q!=end(*p);++q){
    ....
  }
}

#3 运算符

3.1 一些概念

  • 一元运算符,unary operator
  • 二元运算符,binary operator
  • 组合运算符和运算对象
  • 运算对象转换 状态提升
  • 重载运算符 overlaoded operator
  • 左值右值
  • 复合表达式
  • 括号无视优先级与结合率
  • 优先级与结合率的影响
  • 求值顺序

3.2 算数运算符

  • +,-,*,%

3.3 逻辑和关系运算符

  • !,<,<=,>,>,==,!=,&&,||

3.4 复合运算符

  • +=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=

3.5 递增递减

  • ++
  • --

3.6 成员访问运算符

string s1 = "a string",*p = &s1;
atuo n = s1.size();
n = (*p).size();
n = p->size();

3.7 条件运算符

  • cond?exp1:exp2;

3.8 嵌套条件运算符

finalgrade = (grade > 30)?" high pass": ((grade < 60) ? "fail" : "pass");

3.9 在输出表达式中使用条件运算符

cout<< ((grade < 60)?"fail":"pass");

3.10 位运算符

  • - 位求反 1 变 0,0 变 1 ;11=00,00=11
  • << 左移
  • >> 右移
  • & 位与 ;两个都为 1 则位 1,否则为 0; 11=1,10=0,00=0;
  • ^ 位异或 ;两个有且仅有一个为 1 则为 1,否则为 0; 11=0,10=1,00=0;
  • | 位或 ;至少有一个为 1 则为 1,否则为 0; 10=1;00=0;11=1;

3.11sizeof

sizeof 返回的是表达式结果类型的大小;

3.12 逗号运算符

vector<int>::size_type cnt = ivec.size();
for(vector(int)::size_type ix =0;ix!=ivec.size();++ix,--cnt){
  ivec[ix] = cnt;
}

3.13 类型转换

// double -> int
// 隐式转换
int ival = 3.541 + 3;

3.14 算数转换

bool flag;  char cval;
short sval; unsigned short usval;
int ival;   unsigned int uival;
long lval;  unsigned long ulval;
float fval; double dval;


// 基本遵循规则,小转大后面补0,大转小损失n位后的精度
3.14159L + 'a'; a提升为int,int转换成long double
dval + ival; ival 提升为 double
dval + fval; fval 提升为 dval
ival = dval; double 去精度隐式转换为int
flag = dval; 0 false,1 true
cval + fval; cval提升为int,最后转换为 float
sval + cval; 都提升为int
cval + lval; cval 转换成 long
ival + ulval; ival 转换成unsigned long
usval + ival; 根据unsigned short  int 所占空间大小进行转换
uival + lval; 根据unsigned int  long 所占空间大小进行转换

3.15 其他隐式类型转换

数组转换成指针

int ia[10];
// ia 转换成 指向数组首元素的指针
int *ip = ia;
  • 指针的转换
char *cp = get_string();
// 判0
if(cp){}
// 判空
while(*cp){}
  • 类类型定义的转换
string s = "";
while(cin>>s){
...
}
  • 转换成常量
int i;
// *int -> const *int
const int &j = i;
const int *p = &i;

3.16 显示转换

int i,j;
double slope = i/j;

3.17 强转

  • cast-name expression;

cast-name 为

  • static_cast; 任何具有明确定义的类型转换; double slope = static_cast(j)/i
// 使用static_cast 找回存于 void*指针
void *p = &d;
double *dp = static_cast<double*>(p);
  • dynamic_cast
  • const_cast; 只能改变运算对象的底层 const
const char *pc;
// 去const
char *p = const_cast<char*>(pc)
  • reinterpret_cast; 运算对象的位模式提供较低层次上的重新解释
int *ip;
// int* -> char*
char *pc = reinterpret_cast<char*>(ip);

3.18 老旧的强制转换

  • type(expr);
  • (type) expr;

4.语句

直接写 demo 了

// 流控
if(expr){}else if(expr2){}else

switch(n){
  case 1:
    break;
  default:
    break;
}

// 迭代

while(expr){}

for(expr;expr;expr){}

for(declaration: expression){
  statement
}

do{}while()

// 跳转

break;
continue;


// goto
end: return;
goto end;

// 异常

try{}
catch(execption){}
catch(execption){}

// 异常类型
exception 常见异常
runtime_error 运行时错误
range_error 值超了
overflow_error 上溢
underflow_error 下溢
logic_error 逻辑错误
domain_error 参数对应的结果不在
invalid_argument 参数无效
length_error 长度超了
out_of_range 超出有效值范围

5. 函数

  • 可变参
  • 重载
  • 内联函数
  • 断言
  • NDEBUG 变量
  • 函数指针
int fact(int val){
  return val;
}

void nothing(){
}

// 重载

int a (){}
int a(int b){return b;}

// 可变参
initializer_list<T> lst;

void error_msg(initializer_list<string> args){
  for(auto beg=args.begin();beg != args.end();++beg){
      cout << *beg << "\n" << endl;
  }
}


// 内联函数
constexpr int asdf (){return 1}
constexpr int foo = asdf();


// 断言
// 假就退出,真就继续运行
assert(expr);

// NDEBUG类似py
// 输出函数名称
cout << __func__ << endl;
cout << __FILE__ << endl;
cout << __LINE__ << endl;
cout << __TIME__ << endl;
cout << __DATE__ << endl;

// 函数指针
// 首先是函数
bool lengthCompare(const string &,const string &);
// 然后声明一个存储函数的指针
bool (*pf)(const string &,const string &);
// 使用指针存储/指向函数
pf = lengthCompare;
pf = &lengthCompare;
// 调用
pf("asd","asdf");
(*pf)("asd","asdf");
// 重载函数指针
void ff(int*);
void ff(unsigned int);
// 自动指向第二个函数
void(*pf1)(unsigned int) = ff;
// 指向第一个函数
void(*pf2)(int*) = ff;

// 函数参数
void useBigger(const string &s1,const string &s2,bool pf(const string &,const string &));
void useBigger2(const string &s1,const string &s2, bool (*pf)(const string &,const string &));
// 传一个函数进去
useBigger2("123","1234",lengthCompare);
// 返回函数指针

using F = int(int*,int)
using PF = (int*)(int*,int);
// 声明函数返回函数
PF f1(int);
F *f1(int);
// 直接调用
f1(1)(1,2);
// 
string::size_type sumLength(const string&,const string&);
string::size_type largerLength(const string&,const string&);
decltype(sumLength) *getFcn(const string &);

6.类

#include <iostream>

using namespace std;
// Shape 就是一个抽象类
class Shape
{
// 外部可访问内容
public:
Shape(){};
// 构造函数
Shape(double x, double y)
{
width = x;
height = y;
};
// 虚函数,需要子类实现,必须给默认值
virtual double getArea() = 0;
// 通用函数,可重写
void setWidth(double w)
{
width = w;
};

void setHeight(double h)
{
height = h;
};

// 被保护的部分
protected:
double width;
double height;

// 私有部分
private:
double total;
};

class ChangFangXing : public Shape
{
public:
double getArea()
{
return width * height;
}
};

class Yuan : public Shape
{
public:
double getArea()
{
return 3.14 * (width * width);
};
};

int main(void)
{
ChangFangXing c;
c.setWidth(10.00);
c.setHeight(10.00);
cout << c.getArea() << endl;

Yuan a;
a.setWidth(10.00);
a.setHeight(10.00);
cout << a.getArea() << endl;
return 0;
}

标题:C++ Primer 手记
作者:devcui
地址:https://blog.eiyouhe.com/articles/2021/04/12/1618220592500.html