C++ Primer 手记

本贴最后更新于 1463 天前,其中的信息可能已经沧海桑田

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; }

第二类类

#ifndef __SALE_HEAD__ #define __SALE_HEAD__ #include <iostream> using namespace std; struct Sales_data{ // 编号 string bookNo; // 总销售收入 double revenue = 0.0; // 某本书的销量 unsigned units_sold =0; string isbn() const{return bookNo;} Sales_data& combine(const Sales_data&); // 平均价 double avg_price() const; }; Sales_data add(const Sales_data& , const Sales_data& ); ostream& print(const ostream& ,const Sales_data& ); istream& read(istream& ,Sales_data&); #endif
  • struct 结构体,也可以看作类

  • struct 与 c 不同允许有内部函数

  • 访问内部变量实际上通过 this

    xxx.isbin() 实际上 Sales_data::isbn(&xxx)将调用者指针传入 然后用指针访问调用者的内部值 std::string isbn() const { return this->bookNo; }
  • 默认情况下 this 为非常量指针,比如在 Sales_data 中指针为 Sales_data * const,为了防止 this 转变指向对象,所以在形参列表后加入了 const 如此指针类型为 const Sales_data * const 改变指针指向将会报错

  • std::string Sales_data::isbn(const Sales_data *const this) 将会报错,因为试图改变函数内 this 的指向

6.1 抽象

  • 虚基类 开头演示过例子了
  • 虚继承
class A { public: void setx(int a){x = a;} int getx(){return x;} private: int x; }; class B: public A { public: void sety(int a){y = a;} int gety(){return y;} private: int y; }; class C: public A { public: void setz(int a){z = a;} int getz(){return z;} private: int z; }; class D: public B, public C { //...... };

B 继承了 A,C 继承了 A,B 和 C 都有 A 的 X,那么 D 继承了 B,C 所有 A 中的 x 变量在 D 中有两份

#include <iostream> using namespace std; class A { public: void setx(int a){x = a;} int getx(){return x;} private: int x; }; class B: virtual public A { public: void sety(int a){y = a;} int gety(){return y;} private: int y; }; class C: virtual public A { public: void setz(int a){z = a;} int getz(){return z;} private: int z; }; class D: public B, public C { //...... }; int main() { D test; test.setx(10); cout<<test.getx()<<endl; return 0; }

如上为虚继承,最后 D 只有一份 A 中的函数和变量

6.2 封装

  • public 公开
  • private 私有
  • protected 受保护,派生类中可使用

了解各自的作用域,private 提供 getter setter 就是封装了。

6.3 继承/派生

#include<iostream> using namespace std; // 基类 class base { public: base(){x = 0; y = 0;} base(int a){x = a; y = 0;} base(int a, int b){x = a; y = b;} private: int x; int y; }; class derived: public base { public: // 派生类初始化时需要调用基类的构造函数来初始化xy derived():base(){z = 0;} // 派生类用三个参数,传入了基类中ab来初始化xy,剩下的初始化派生类中的z derived(int a, int b, int c):base(a,b){z = c;} private: int z; }; int main() { derived A; derived B(1,2,3); return 0; }

6.4 构造函数/析构函数

#include <iostream> using namespace std; class A { public: A(){cout<<"A constructor"<<endl;} ~A(){cout<<"A destructor"<<endl;} }; class B: public A { public: B(){cout<<"B constructor"<<endl;} ~B(){cout<<"B destructor"<<endl;} }; class C: public B { public: C(){cout<<"C constructor"<<endl;} ~C(){cout<<"C destructor"<<endl;} }; int main() { C test; return 0; }

运行结果如下:
A constructor
B constructor
C constructor
C destructor
B destructor
A destructor

程序运行结果很好地说明了构造函数和析构函数的执行顺序。构造函数的执行顺序是按照继承顺序自顶向下的,从基类到派生类,而析构函数的执行顺序是按照继承顺序自下向上,从派生类到基类。

因为每一个类中最多只能有一个析构函数,因此调用的时候并不会出现二义性,因此析构函数不需要显式的调用。

转型构造函数

class Age { public: Age(int a){age = a;} private : int age; }

传入 age,进行初始化后返回的为对象类型

6.5 多态

/* * @Author: cuihaonan * @Email: devcui@outlook.com * @Date: 2021-04-20 17:16:22 * @LastEditTime: 2021-04-20 17:16:23 * @LastEditors: cuihaonan * @Description: Basic description * @FilePath: /kube-console/demo.cpp * @LICENSE: NONE */ #include <iostream> using namespace std; class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } int area() { cout << "Parent class area :" <<endl; return 0; } }; class Rectangle: public Shape{ public: Rectangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Rectangle class area :" <<endl; return (width * height); } }; class Triangle: public Shape{ public: Triangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Triangle class area :" <<endl; return (width * height / 2); } }; // 程序的主函数 int main( ) { Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // 存储矩形的地址 shape = &rec; // 调用矩形的求面积函数 area shape->area(); // 存储三角形的地址 shape = &tri; // 调用三角形的求面积函数 area shape->area(); return 0; }

6.6 命名空间

namespace Li{ //小李的变量声明 int flag = 1; } namespace Han{ //小韩的变量声明 bool flag = true; }

6.7 友元函数

#include<iostream> using namespace std; class book { public: book(){} book(char* a, double p); // friend 将下面的函数实现变为友元函数 // 所以display可以访问 private friend void display(book &b); private: double price; char * title; }; book::book(char* a, double p) { title = a; price = p; } void display(book &b) { cout<<"The price of "<<b.title<<" is $"<<b.price<<endl; } int main() { book Alice("Alice in Wonderland",29.9); display(Alice); book Harry("Harry potter", 49.9); display(Harry); return 0; }

6.8 静态成员静态函数

静态成员使用需要初始化

class student { public: student(){count ++;} ~student(){count --;} private: static int count; //其它成员变量 }; int student::count = 0;

静态函数使用需要初始化

#include<iostream> using namespace std; class test { public: static void add(int a); }; void test::add(int a) { static int num = 0; int count = 0; num += a; count += a; cout<<num<<" "<<count<<endl; } int main() { test one,two,three; one.add(5); two.add(4); three.add(11); return 0; }

static 为所有类/派生类共享变量/函数,累加

6.9 内存分配

#include<iostream> using namespace std; class test { public: test(int i = 1){num = i;cout<<num<<" Constructor"<<endl;} ~test(){cout<<num<<" Destructor"<<endl;} private: int num; }; int main() { // new 相当于malloc test * t0 = new test(0); test * t1 = new test[5]; test * t2 = (test *)malloc(sizeof(test)); // delete相当于free delete t0; delete[] t1; free(t2); return 0; }

6.10 重载,覆盖,遮蔽

函数重载是指两个函数具有相同的函数名,但是函数参数个数或参数类型不同。函数重载多发生在顶层函数之间或者同一个类中,函数重载不需要构成继承关系。

覆盖构成条件和多态构成条件是相同的,覆盖是一种函数间的表现关系,而多态描述的是函数的一种性质,二者所描述的其实是同一种语法现象。

函数遮蔽同样要求构成继承关系,构成继承关系的两个类中具有相同函数名的函数,如果这两个函数不够成覆盖关系,则就构成了遮蔽关系。遮蔽理解起来很简单,只要派生类与基类中具有相同函数名(注意不是相同函数签名,只需要相同函数名就可以了)并且不构成覆盖关系即为遮蔽。

  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖 • 3 关注
5 操作
someone66251 在 2021-04-20 17:20:03 更新了该帖
someone66251 在 2021-04-18 22:37:50 更新了该帖
someone66251 在 2021-04-18 20:24:29 更新了该帖
someone66251 在 2021-04-18 19:42:55 更新了该帖 someone66251 在 2021-04-13 23:44:00 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...