My Object Oriented Programming Habits
- class中的数据变量英文命名写全并且每个单词首字母大写,构造函数传参数时参数名称全大写
- private数据成员在public中获得函数用get_Data()命名
记录期末之前上课&题目
5.24 W14 1-1
- function<returntype(parameters)> 类型可以统一函数指针和函数对象,如果将function<int(int)>作为函数参数则可以将所有以int返回值int参数的函数或者int返回值int参数的对象都可以传入并不违法【/统一了函数对象和函数指针/】
5.29 HW14-15
- string.c_str() 返回一个
const char*为string内部存储的数组结尾是\0,为了保证不用外部char指针修改string这个const无法初始化char*类型。将string转换成char*应该用int copy(char* tar,int lenth,int pos=0)返回实际拷贝的字符数目。 - string类的拼接操作
+时间复杂度为生成的字符串长度,用+=更快
5.31 W15 1-1
- string拼接操作速度:+= > string.append( string) > stringstream > +
5.31 Review L1-L3
- 带缺省值参数函数不算参数个数,去掉缺省值参数后如果其他参数都一样会产生歧义被拒绝
- auto后面定义的变量要同一类型
auto i=0; and decltype(i) b;即decltype() xxx为类型说明符,后面可以带个参数为“定义一个和这个参数一样类型的变量xxx”- auto作为函数返回值类型要在参数列表后面加
->decltype(returnvalue表达式) - NULL是int型常量0 nullptr是严格空指针
- Function
f1(int a,int b=1)andf1(int a=2)即使一个是private不许访问但是仍然会ambiguous二义性错误。 - inline ReturnType Function();
- 遍历某个范围的for循环
1 | int arr[3] = {1, 3, 9}; |
5.31 Review L4
初始化列表顺序并不是初始化顺序,初始化列表时应用声明顺序进行初始化
后缀运算符++需要哑元dummy,要把原本的数据增加之后返回的值仍然是增加前的,可以先复制原对象然后给原对象++,最后返回原对象的复制。
=,【】,(),->这些只能通过成员函数来重载
流运算符重载
1
2
3
4istream& operator>> (istream& in, Obj& dst );
ostream& operator<< (ostream& out, const Obj& src );
需要声明为类的友元以便访问私有对象,在类外(全局)进行重载的定义
friend istream& operator>>(istream& in, Obj& dst);
5.31 Review L5
友元函数可以是其他类的构造、析构函数,也可以是其他类,也可以是全局函数
static普通静态变量与函数:初定义要初始化仅一次,内部可链接,作用域仅限声明文件cpp不能被其他cpp所用
static静态成员变量:归属于类而不是具体对象,所有对象可共享。通过
obj.var和classname::var均可访问。在实现文件赋初值VarType ClassName::static_var=Value.需要在h中声明cpp中定义,h定义会导致包含h后重复定义
static静态成员函数:static关键字在ReturnType前,类和obj都可以访问,不能访问非静态成员,即属于整个类的函数只能访问类变量
常量数据成员:初始化后在该obj整个生命周期中不能改变
常量成员函数:
const关键字在函数体前Function() const{...}不能修改class的成员,只读而不可写入,常量对象只能调用const成员函数拷贝(传递参数)时如果有指针成员容易出错,直接位拷贝指针不变而同一块内存会释放两次
5.31 Review L6
不想给某个函数修改值的权限则使用const参数
拷贝构造函数参数:同类对象的常量引用 自动合成为Bitwise Copy遇指针出错
常(左值)引用可以绑定右值,引用本身为左值
右值引用在延续即将销毁变量的声明,提升处理效率,即把本要销毁的变量引用了变成了不销毁的左值——移动构造函数参数:同类右值引用
移动构造函数需要:1.复制指针地址或者值 2.将原指针地址改0避免释放
使得生成的临时对象被保留下来,本该释放(析构)时发现指针为空,不能释放内存,而真正有意义的指针(数据)被移动保留了下来
原则:不浪费任何右值(创造右值之后不销毁,及时利用)
std::move(左值),把左值当做右值用,需要这个左值不再被使用或者即将被改(swap)
类型转换函数的自定义 从Src到Dst类
1 | class Src{ |
5.31 Review L7
- 构造函数和析构函数,复制运算、友元不能被继承
- use
using关键字来使用基类的(所有)构造函数 - 基类的私有成员在派生类函数中也不能访问,只有基类的成员函数中被访问。而公有成员成为派生类共有成员可以被访问。protected成员可以在派生类成员函数中访问但不能在外部函数访问
- 重载Overload,函数名必须相同,函数参数必须不同,作用域相同。(编译多态或者属于静多态)
- 重写隐藏其实就是Redefining,重新定义基类函数实现特殊功能,屏蔽了所有同名函数,参数可以同可以不同(运行时多态、动态多态)using 类::成员函数来解除屏蔽)
6.1 Review L8
- 凡是可以接受基类ptr或obj,都可以直接接受派生类,会自动切片
- 通过对象的向上类型转换可以访问private继承无法访问的基类成员变量coz向上类型转换只可以Public继承,不允许私有继承向上转换
- 带有虚函数的class,对象大小会加上一个指针(8),多个虚函数也只有一个Vptr指向vtable
- 虚函数机制在构造、析构函数中不工作,构造不能虚而析构经常是虚的总是将基类的析构函数设置为虚
- 虚函数Feature重写覆盖必须要函数名参数表一模一样,这样会根据运行时实际类型进行动态晚绑定
- 在函数体前面加override关键字检查是否成功重写覆盖
- 函数体前加final关键字——禁止继承后再覆盖,已经是最终版本的函数实现
6.1 Review L9
- (形参)之后加
=0声明为纯虚函数,含有一个纯虚即为抽象类无法具象化出对象,只为了之后的继承类实现共性“接口”或者“方法”。一般是没有实际物理意义的抽象(shape,creature) - 纯虚函数必须被之后要使用的继承类重写覆盖,不然仍然是纯虚函数而该类为抽象类,即必须把所有纯虚函数实现一遍
- 纯虚析构函数仍需要函数体,即声明纯虚
=0之后还要写一个空实现函数体,后面不用显式地重写覆盖 - 使用基类指针数组管理具体派生类对象,具体处理时转化成专用指针调用专用接口
- 使用
dynamic_cast<tar*or&>(obj_p or obj_r)如果失败返回nullptr,用来判断实际类型 static_cast不检查实际类型,只要有继承关系就转,dynamic会动态检查,慢但是安全- 向上类型转换:指针和引用时不改变虚函数表(晚绑定,仍按实际类型调用继承重写函数)而直接切片为基类对象会丢失增加数据和方法
- dynamic_cast正是通过虚函数表判断是否安全地进行向下类型转换
- 多重继承:Best Practice 最多继承一个非抽象类,可以继承多个抽象类(接口)
- 多态(接口函数根据实际派生类而不同表现)效果条件:继承&&虚函数&&(引用or指针)
- 模板:算法实现与参数类型无关,则将函数的参数类型定义为一种特殊的“参数”
- 调用函数模板时,可以手动输入“模板参数”也可以让编译器自己认。实现原理:编译期将模板替换为需要的类型生成一份对应的函数代码,需要多少不同的就生成多少
- 模板使用:
1 | tmplate <typename T> |
- 类模板中成员函数类外定义前要声明一下同样的模板,模板参数编译期确定不可使用变量
- 模板也是同一段代码实现不同的相似功能,但是在编译期处理为“静多态”





6.1 Review L10
- 标准C++库所有内容都在标准命名空间std里,包括stl标准库
- 不同命名空间中的同名函数、变量等命名不会互相冲突
- 自定义命名空间
namespace A{int x,y;}使用时A::x and A::y,用using namespace直接使用这个命名空间的所有成员,也可以using namespace::var or ::func 直接使用某几个函数 - Containers: 简单、序列vec,list、关系set,map
1 | template<class T1, class T2> struct pair{ |
支持 .first .second make_pair( , ) < > 按照第一第二比较
tuple:类似pair,无限长,使用v0=get
(tuple_var)且pos需要编译确定,不能使用变量 支持make_tuple(,,,) tie(x,y,z)=make_tuple(xx,yy,zz)即tie返回左值引用的元组,可以利用右侧的tuple来一次性多重赋值
vector支持:vec[pos] vector
vec0 .size() .clear() .push_back( xx) .pop_back(xx) use iterator to .erase(x.begin()+1,num) .insert(iterator)而iterator支持*,可以直接sort,支持++ – += -=支持减法运算 迭代器失效:insert和erase,和改变大小时可能会扩展capacity而整体迁移所有it失效,修改过容器后不使用之前的迭代器
list:快速的增减push_front() push_back() find(l.begin(),l.end(),tar) l.insert(it,tar) 不支持下标,插入和删除时不相关迭代器不失效
set:不重复元素构成的无序集合,只是不保持加入顺序,内部按大小排列
支持insert() 查询find()返回迭代器 erase(s.find(val)) count(val)只有0或1
map:关联映射 其实为<Key,T> Key必须互不相同 通过下标map[key]访问val如果不存在则创建对应映射 支持insert(pair)插入 查询find(key)返回it 统计count(key)要么0要么1 支持erase(it)
6.1 Review L12
- 构造方式s3(“string”,num)num为截取的长度(num,’x‘)赋值num个x,(it1,it2)迭代器
- string.c_str()返回一个
const char*不能修改,涉及底层实现,不能赋值给char*需要copy(ptr,n,pos=0) - size()orlength() clear() empty() push_back(‘a’) or append(s2) 字典序比较
- cin>>读到空格 getline(cin,str)读一行不包括/n getline(cin,str,’#’)可以读换行一直到终止符#
- stoi(“2001”) stoi(“50 cats”,&sz)sz为读入长度 stoi(string,nullptr,进制=0)默认自动检查进制,可以stod转化double
库中格式控制函数fixed scientific setprecision(2) oct dec setw(3) setfill(’‘)设置对齐长度为3且填充字符 - 使用stringstream拼接字符串,也是stream可以赋值于var ss>>a>>b
- 正则表达式https://www.runoob.com/regexp/regexp-intro.html菜鸟教程https://blog.csdn.net/bgzclxqq/article/details/90262904 CSDN关于C++的正则式
6.1 Review L13
声明函数指针
ReturnType (*ptrvar)(parameters),可以直接赋值,甚至可以直接auto指定类型less
()和greater ()为自带的比较函数,其实是个对象,长得像个函数 greater是一个模板类
greater
用int实例化的类 greater
() 该类的一个对象 函数对象需要public重载()运算符
把函数当参数需要声明:
template <class Compare> void sort(Compare comp)可以接受函数指针和函数对象智能指针
shared_ptr<T> p1(new int (1))也可以make_shared(2) 或者拷贝构造同类 重载了*运算符直接访问对象 也可以->访问成员
自带引用计数use_count()引用全归0才自动销毁对象
实现原理:

获取常规指针.get() 清楚并减少引用.reset() 可以用static或dynamic_cast<>(p)
不能使用同一个裸指针初始化多个智能指针 即辅助指针U_ptr只能有一个不然多次释放
存在循环引用永不析构问题weak_ptr指向对象但是不计数wp.lock()转换智能指针
unique_ptr确保一个对象只能被一个指针指向(引用) 不能赋值指针,可以移动up2=std::move(up1) 使用release放弃up控制权并返回裸指针
6.1 Review L14
模板方法:抽象出不同具体类的骨架,写出通用的基类并且将步骤的实现延迟到子类中(设置为纯虚函数)。不改变算法结构而重新定义一些实现步骤。
父类定义骨架,子类实现细节,需要新的实现重新继承即可

策略模式(strategy):定义一系列算法并封装,可以互相替换


- 对不同的每个子任务(方法)都写出基类Strategy然后继承出具体实现类,实际调用的框架中只需要管理(存储)这些方法的基类Strategy指针即可
- 策略模式符合“单一责任”原则:一个类or接口只负责一项职责,功能层面解耦
- .迭代器模式:基于Iterator基类,实现不同聚合对象的顺序访问,使得接口统一而使用方法统一
6.8 Review L15
结构性模式——适配器Adapter、代理委托Proxy、装饰器Decorator
适配器模式:客户期待接口基类Stack中有各种接口,现在有一个写好的类似功能类Vector,想加一层Adaptec使得代码继续复用
Adaptee——Adapter——Target——Client
实现:
组合——把Adaptee放到Adapter中当成员,继承Tar基类,挨个实现要求的函数
继承——公有继承Tar和私有继承Adaptee类,即er类就是ee和tar类,实现函数时直接调用ee中继承过来的函数即可
代理委托Proxy:客户要求的基类接口,有一个RealSubject继承了基类接口可以满足需求,但是需要更多功能和控制操作
Client—BaseSub—Realsub<—Proxy:BaseSub
Programer—ProxySub(derived from BaseSub)has a RealSubPtr
实现BaseSub中的函数,函数体{Ptr->Request; other commands;}实现了包装并增加。
装饰器Decorator:无限的增加新功能,每次增加几个功能并可以无限进行这个装饰的过程。装饰器是定义一种递归模式,方便之后的功能继承这种模式
实现:装饰器:public Base 私有基类指针,构造函数接受一个基类指针(这一层装饰之前的类的指针)并存储。定义虚函数addon()为每一层装饰的新功能,定义常规需求函数Draw{add(),ptr->draw()}。具体增加功能的时候需要功能类继承装饰器类然后实现add函数为新功能
魔兽争霸学技能:学技能类(Decorator) 具体学了什么(继承Decorator的类)构造,一级级学技能上去。
1
2
3
4
5
6
7
8
9
10
11
12class Decorator::public base{
base* upper;
public:
virtual ReturnType addon(){}
ReturnType Request(parameters){
addon();
upper->Request;
}
};
//crearte a decorator pattern构造:基类—装饰递归结构—装饰1—装饰12—装饰123
调用过程(解嵌套):装饰123—3Draw,指针变为装饰12—2Draw指针变为1—1Draw,指针变为基类Draw
6.10 FinalExample:
不能继续使用(using)纯虚基类的构造函数
使用虚函数机制时把基类析构函数生命为virtual
list的迭代器是双向的,甚至可以反向迭代。反向迭代器rbegin()和rend()其自增运算++为向头变换
迭代器失效仅有erase操作的迭代器,其他任何迭代器不受影响。而且list迭代器太牛了,锁定自己的元素不变,也就是说指向链表中的某一个元素。头迭代器和尾迭代器在中间插入元素后依然是头尾不需要改变。
list中的insert是在迭代器it之前的位置插入元素,并且迭代器仍然指向原来元素(链表特性)
list为空时,用insert比较危险——头尾迭代器相等并且会一起指向尾空元素。不要把list想象成动态数组,还是想象成链表安全些。
关于Strategy模式:管理一堆Strategy_x基类,每个决定单独的x1、x2、x3
关于map类,声明:typedef map<int,string> num2name; 使用时可以用数组符号修改key值也可insert(),但是insert不支持已经存在的key,数组下标[]会覆盖。