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 重载,覆盖,遮蔽
函数重载是指两个函数具有相同的函数名,但是函数参数个数或参数类型不同。函数重载多发生在顶层函数之间或者同一个类中,函数重载不需要构成继承关系。
覆盖构成条件和多态构成条件是相同的,覆盖是一种函数间的表现关系,而多态描述的是函数的一种性质,二者所描述的其实是同一种语法现象。
函数遮蔽同样要求构成继承关系,构成继承关系的两个类中具有相同函数名的函数,如果这两个函数不够成覆盖关系,则就构成了遮蔽关系。遮蔽理解起来很简单,只要派生类与基类中具有相同函数名(注意不是相同函数签名,只需要相同函数名就可以了)并且不构成覆盖关系即为遮蔽。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于