C++ Primer 手记

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

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(); sizeconstexpr函数时才是正确的声明语句

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++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    106 引用 • 152 回帖
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 更新了该帖

相关帖子

欢迎来到这里!

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

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