那个学C++不没有点大病?一点点癫狂的语法混乱版note和有一点点长的无语的标题,让人怀疑精神状态尼奥

一、C++基础语法

类型转换

切勿混用无符号类型和有符号类型
表达式中两者都有时,有符号类型会转化为无符号类型,当该值为负时会出现非预期结果;

unsigned a = 1; int b = -1; cout<<a*b;//输出 4294967295
//详解: b的源码:100...1 负数转补码按位取反加1后为:1111...0  无符号类型把该值读出为4294967295 (结果视所在机器位数而定)

无符号类型用于循环时注意其永远不会小于0的特性

转义序列和指定类型

在这里插入图片描述
在这里插入图片描述
注意:“\1234” 前3个构成转义序列八进制对应的asill值

在这里插入图片描述
注:表示长整型时用L而不是小写 l 它和 1 容易弄混;

后缀的存在是为了明确指示字面值的数据类型,从而避免编译器的隐式类型转换或产生二义性。然而,并非所有数据类型都需要后缀,因为它们的类型可以根据字面值的形式自动推断出来。此外,使用错误的后缀可能导致编译错误或运行时错误,因此需要小心使用。无语了;

在这里插入图片描述

(a) 字符  宽字符型字符(wchar_t类型)  字符串  宽字符型字符串
(b) 整数  无符号数  长整数  无符号长整数  八进制数  十六进制数
(c) 浮点型   单精度浮点型   long double类型的扩展精度浮点型 
(d) 整数  无符号整数 浮点数  科学计数法表示的浮点数

变量初始化 不等于 赋值 (初始化是创建变量并赋予一个初始值,赋值是擦除当前值用新值代替)
变量声明 规定了变量类型和名字,定义 除此之外还申请了存储空间,可能还会赋初始值。(建议初始化每一个内置类型的变量)

extern int i; //声明 i 
int j;//声明并定义 j
extern int k = 3; //赋初始值抵消了extern的作用变成了定义,且如果是在函数内部初始化extern标记的变量会报错

变量必须且只能在一个文件中定义一次,其他文件中使用到该变量的必须进行声明,却绝对不能重复定义!(变量能且只能被定义一次,但是可以被多次声明)

标识符的命名规范
由字母、数字、下划线组成,且必须以字母或下划线开头。
不能作标识符规范

  • 一些作为C++语言的使用的关键字,如if、int、false等
  • 一些作为C++标准库的保留字,不能连续两个下画线,不能下画线连大写字母开头,
  • 定义在函数体外的标识符不能以下划线开头

推荐的标识符规范

  • 见名知义,能体现实际含义好懂
  • 变量名小写字母,如 index
  • 类名大写字母开头,如Index
  • 标识符多字母组成采用下划线分割或大写字母开头分割,如student_loan_up或studentLoanUp

作用域
全局作用域和块作用域
作用域嵌套,外层作用域声明某名字,其嵌套的作用域也就是内作用域都能访问该名字,同时允许内层作用域重新定义外层作用域已有的名字。

引用和指针的主要区别

  1. 引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
  2. 不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是 NULL)。
  3. 一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

https://www.runoob.com/w3cnote/cpp-difference-between-pointers-and-references.html

使用未经初始化的指针容易引发错误,因此不知道指向具体对象时初始化为nullptr ,如:int *p = nullptr;
因此要判断一个指针是否指向合法地址,判断是不是nullptr即可,或者如果没有初始化指针的话可以try…catch是否出错。

void* 是一种特殊的指针类型,可以存放任意对象的地址。

int* p和 int *p的问题,其实基本类型是int的p是指向int的指针;所以建议采用第二种写法,int *p1, *p2;

指向指针的指针,为了访问原始对象,需要解引用两次;
指向指针的引用,引用本身不是对象因此不能定义指向引用的指针,但是指针是对象,所以存在对指针的引用;

int i = 33;
int *p;//int 类型的指针p
int *&r = p;//从右往左看,r 是 对指针p的引用
r = &i;//r是指向p的引用,给r赋值&i 就是让p指向i
*r = 0;//解引用 r 得到 i,也就是p指向的对象,改i的值为0

面对复杂的指针或引用声明语句时,从右向左阅读有助于弄清真实含义;

定义一个const变量在多个文件中声明并使用它的方法是,不管是定义还是声明都加extern关键字;

extern const int bufsize = fcn();//1.cc定义并初始化一个常量,并让该常量能被其他文件访问

** 底层const和顶层const? **

常量表达式:值不会改变并且编译过程就能得到计算结果的表达式
比如 const staff_size = get_size(); 就是不是,因为其到运行时才能获取到,const int limit = 20;才是常量表达式

C++11 :如果你认定变量是一个常量表达式,那就把它声明成 constexpr 类型;

auto类型推导
1、使用引用作初始值时,以引用对象的类型作为auto的类型;
int i = 0, &r = i;
auto a = r; //a 类型为 int
2、auto会忽略顶层const、保留底层const;
auto &h = 42; //非常量引用需要绑定一个可修改的对象;
const auto &j = 42;

Everything should be made as simple as possible, but not simpler

类是对象的抽象和统称,对象是类的实例;

浅拷贝:只拷贝指针地址,C++默认拷贝
构造函数与赋值运算符重载都是浅拷贝;
节省空间,但容易引发多次释放;
深拷贝:重新分配堆内存,拷贝指针指向
内容。
浪费空间,但不会导致多次释放;

写时复制

二、C++指针

未初始化和非法的指针,用指针间接访问一定要非常小心确保已初始化并恰当的赋值!
1、如果开始不知道或者不用的时候指向何处可以初始化为null
2、在对一个指针间接引用前,确定指针的值是否为null;

int *a;//未初始化化
*a = 12//非法访问

运气好定位到非法地址,程序出错终止,运气差定位到一个可以访问的地址并修改了它,错误难以捕捉,引发的错误可能完全想不到!

左值和右值
在这里插入图片描述
=号左边为左值,=号右边为右值。左值取的是如上图cp+1处的内存空间,右值取的是内存空间的值;

char ch = 'a';
char *cp = &ch;

关于++++,----等运算符:
编译器程序分解成符号的方法是:一个字符一个字符的读入,
如果该字符可能组成一个符号,那么读入下一个字符,一直到读入的字符
不再能组成一个有意义的符号。这个处理过程称为“贪心法”。
例如:int a=1,b=2;c;
c=a+++b;/∥相当于a+++b
d=a++++b;/∥相当于af+++b,error

在这里插入图片描述
代码和数据在C++程序中的存储;

在这里插入图片描述
在这里插入图片描述

RAlI(Resource Acquisition Is Initialization) :
C++所特有的资源管理方式。有少量其他语言,如Rust也采纳了RAlI,但主流的编程语言中,C++是唯一一个依赖RAII来做资源管理的。RAlI依托栈和析构函数,来对所有的资源——包括堆内存在内进行管理。
对RAll的使用,使得C++不需要类似于Java那样的垃圾收集方法,也能有效地对内存进行管理。RAll的存在,也是垃圾收集虽然理论上可以在C++使用,但从来没有真正流行过的主要原因。
RAll有些比较成熟的智能指针代表:如std:auto_ptr和boost:shared_ptr

在这里插入图片描述

内存泄漏(Memory Leak)问题
什么是内存泄漏问题:
指程序中己动态分配的堆内存由于某种原因程序未释放或无法释,
造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存泄漏发生原因和排查方式:
1.内存泄漏主要发生在堆内存分配方式中,即“配置了内存后,所有指
向该内存的指针都遗失了”。若缺乏语言这样的垃圾回收机制,这样的内存
片就无法归还系统。
2.因为内存泄漏属于程序运行中的问题,无法通过编译识别,所以
只能在程序运行过程中来判别和诊断。

C++中推出了四种常用的智能指针:
unique_ptr、shared_ptr、weak_ptr;
auto_ptr:C++11中已经废弃(deprecated)的在C++17中被正式删除;

在这里插入图片描述

在这里插入图片描述
使用问题
在这里插入图片描述
所有权问题
在这里插入图片描述
循环引用:引用记数带来循环引用问题,导致堆内存无法正常回收,造成内存泄漏问题

在这里插入图片描述

C++引用
在这里插入图片描述

函数中对于基本类型来说,pass by value更高效,对与自定义类型来说,pass by reference to const更高效;

杜绝“野”指针
指向“垃圾”内存的指针。if等判断对它们不起作用,因为没有置NULL;
一般有三种情况:
1.指针变量没有初始化;
2.已经释放不用的指针没有置NULL,如delete和free之后的指针;
3.指针操作超越了变量的作用范围;

指针使用的注意事项:
没有初始化的,不用的或者超出范围的指针请把值置为NULL。(很难啊,指针在多个地方使用,很难确定什么时候不用了,解决方案:智能指针)

空指针是指向null的指针用于内存初始化指针变量,内存编号0~255是系统占用内存不可访问;
野指针是指向非法的内存空间的指针,两者访问应该都会报错,但是在Dev下运行如下代码:编译无误还能运行,只不过会无输出,运行一段时间后窗口自动关闭,这是为什么?

#include <bits/stdc++.h>

using namespace std;

int main(){
	int a = 10;
	int *p = NULL;
	cout << *p <<endl;
	
	int *q = (int *)0x1110;
	cout << *q <<endl;
	return 0;
}

虽然语法无误可以通过编译器,但是由于解引用空指针和野指针导致的内存访问违例,操作系统为了保护系统终止了该程序的执行。

在这里插入图片描述
机器真正的存法:正数直接存,负数保留符号位按位取反+1;
大端序是网络中普遍用的符合人类阅读习惯,小端序大多数个人PC用的,符合机器;

在这里插入图片描述

在这里插入图片描述
这个非常重要一定要注意!!!
在这里插入图片描述
Redis中设计的sdshdr结构体有个len变量存字符串长度,不用遍历去算;有个free管理剩余容量,如果容量不够会自动扩容并修改len大小;

指针和引用

在这里插入图片描述

指针的数组(array of pointers),这个数组里一个个的都是指针 。 T *t[ ]
数组的指针(a pointer to an array),这一个指针指向一个数组。T (*t) [ ]

在这里插入图片描述
第三个例子先看第一个const修饰的左边char表示指针的值不可改,再看第二个const修饰的*,表示指针的指向不能改;

const修饰指针有三种情况

  1. const修饰指针 — 常量指针(指针指向可改,指向的值不可改)
  2. const修饰常量 — 指针常量(指针指向不可改,指针的值可以改)
  3. const即修饰指针,又修饰常量(都不能改)

看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量

const常用来防止误操作

智能指针

堆区的内存一定要手动释放,否则会发生内存泄漏;

解决:智能指针是类模板,在栈上创建智能指针对象,把普通指针交给智能指针对象,当智能指针对象过期时,调用析构函数释放普通指针的内存。

unique_ptr :独享指向的对象
shared_ptr:
wear_ptr:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

三、C++STL库

stl库的六大组件
在这里插入图片描述
六大组件的关系
在这里插入图片描述

容器(container)

容器用于存放数据;
STL的容器分为两大类:
序列式容器(Sequence Containers):
其中的元素都是可排序的(ordered),STL提供了vector,list,deque等序列式容器,而stack,queue,priority_queue则是容器适配器;
关联式容器(Associative Containers):
每个数据元素都是由一个键(key)和值(Value)组成,当元素被插入到容器时,按其键以某种特定规则放入适当位置;常见的STL关联容器如:set,multiset,map,multimap;

#include <bits/stdc++.h>

using namespace std;

struct Display{
	void operator()(int i){
		cout << i << " ";
	}
};

int main(){
	int arr[4] = { 1, 2, 3, 4};
	vector<int> vec(arr, arr+4);//动态数组 
	list<int> lis(arr, arr+4);//链表 
	deque<int> deq(arr, arr+4);//双端队列
	queue<int> que(deq);//队列 
	stack<int> sta(deq);//栈 
	priority_queue<int> prique(arr, arr+4);//优先队列 
	
	for_each(vec.begin(), vec.end(), Display());
	for_each(deq.begin(), deq.end(), [](int i){
		cout<< i << " ";
	});
	
	while(!que.empty()){
		cout << que.front() << " ";
		que.pop();
	}
	while(!sta.empty()){
		cout << sta.top() << " ";
		sta.pop();
	}

	cout<<endl;
	map<string, int> studentlevel;
	studentlevel["level1"] = 1;
	studentlevel["..."] = 3;
	studentlevel["level6"] = 6;
	studentlevel.insert(pair<string, int>("level4", 4));
	for_each(studentlevel.begin(), studentlevel.end(), [](pair<string, int> i){
		cout<<i.first<<":"<<i.second<<endl; 
	});
	
	map<string, int>::iterator iter = studentlevel.find("level6");
	if(iter != studentlevel.end()){
		cout<<"level6 超能力者人数:"<<iter->second<<endl; 
	}
	
	return 0;
}

**非常需要注意:**谨防迭代器失效的问题,比如 iter = studentlevel.earse(‘‘cc’’); 使key为cc的值删除,返回下一个位置的迭代器;
(*it).empty(); 简化后it->empty();

凡是使用了迭代器的循环体,都不要向迭代器所属容器添加元素;
比如push_back可能导致vector对象的迭代器失效;
比如范围for循环向vector添加元素

仿函数(functor)

  • 仿函数一般不会单独使用,主要是为了搭配STL算法使用。
  • 函数指针不能满足STL对抽象性的要求,不能满足软件积木的要求,无法和STL其他组件搭配;
  • 本质就是类重载了一个operator(),创建一个行为类似函数的对象

从函数指针–>泛型->仿函数->仿函数模板;懒

bool MySort(int a, int b){
	return a ‹ b;
}

void Display(int a){
	cout << a <<" ";
}

int main(){
	//C++方式
	int arr[] = {4,3, 2, 1,7};
	sort(arr, arr + 5, MySort);
	for_each(arr, arr + 5, Display);

}

如果数据类型发生变化需要重载很多函数,太麻烦,于是泛型出来了

template<class T>
bool MySort(T const& a, T const& b){
	return a ‹ b;
}

template<class T>
void Display(T const& a){
	cout << a <<" ";
}

int main(){
	//C++泛型
	int arr[] = {4,3, 2, 1,7};
	sort(arr, arr + 5, MySort<int>);
	for_each(arr, arr + 5, Display<int>);
}

接着就是仿函数


struct mySort{
	bool operator()(int a, int b){
		return a ‹ b;
	}
}

struct Display{
	void Display(int a){
		cout << a <<" ";
	}
}

int main(){
	//C++仿函数
	int arr[] = {4,3, 2, 1,7};
	sort(arr, arr + 5, mySort());
	for_each(arr, arr + 5, Display());
}

接着就是仿函数模板,基础类型不需要加,但是对象消耗比较大就可以加

template<class T>
struct mySort{
	bool operator()(T const&  a, T const&  b){
		return a ‹ b;
	}
}

template<class T>
struct Display{
	void Display(T const&  a){
		cout << a <<" ";
	}
}

int main(){
	//C++仿函数
	int arr[] = {4,3, 2, 1,7};
	sort(arr, arr + 5, mySort<int>();
	for_each(arr, arr + 5, Display<int>();
}

算法(algorithm)

STL中算法大致分为四类:包含于,,
1.非可变序列算法:指不直接修改其所操作的容器内容的算法;
2.可变序列算法:指可以修改它们所操作的容器内容的算法;
3.排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作;
4.数值算法:对容器内容进行数值计算;
最常见的算法包括:
查找,排序和通用算法,排列组合算法,数值算法,集合算法等算法

transform();容器计算函数;count统计函数;binary_search();二分查找;

int main(){
	int arr[] = {1, 1, 1, 2, 2, 3, 4, 5, 5, 6};//父序列 
	vector<int> zi(arr+2, arr+6); //子序列 
	int len = sizeof(arr)/sizeof(arr[0]);
	cout << count(arr, arr+len, 1) << endl; 
	cout << count_if(arr, arr+len, bind2nd(less<int>(), 4)) << endl; 
	cout << binary_search(arr, arr + len, 6)<<endl;
	cout<< *search(arr, arr+len, zi.begin(), zi.end()) << endl;
	return 0;
}

手写全排列的实现

输入一个不存在重复字符的全字符串,打印出字符串中字符的全排列
比如输入 123 = 321 = 3!
输出总的情况
123
132
213
231
312
321

#include <bits/stdc++.h>
//f(123)=1+f(23),f(23)=2+f(3),f(3)=3递归
using namespace std;

void swap(char*a, char *b){
	char tmp = *a;
	*a = *b;
	*b = tmp;
}

void permutation(char *ptr, char* postion){
	if(*postion == '\0'){//基准点,退出
		cout << ptr <<endl;
	}
	for (char* pChar = postion; *pChar != '\0'; pChar++){
		swap(*pChar, *postion);//依次和后面的字符交换,比如123,定1位,依次交换2,3
		permutation(ptr, postion + 1);//递归下去
		swap(*postion, *pChar);//别忘了换回来
	}
}

int main(){
	char test[] = "321";
	permutation(test, test);
	return 0;
}

必须保证数组顺序,比如next是从小到大,自己实现的就不会丢失排列,prev是从大到小的顺序
stl中的全排列函数next_permutation()以及prev_permutation();

sort
STL的sort算法,
数据量大时采用QuickSort(快速排序),分段归并排序。
一旦分段后的数据量小于某个门槛(16),为避免QuickSort的递归调用带来过大的额外负荷,就改用InsertSort(插入排序)。
如果递归层次过深,还会改用HeapSort(堆排序)。

迭代器(iterator)

类似智能指针,手写gc小案例,其中使用了迭代器的方法;

开源代码MyGC
在这里插入图片描述

Int main0
GCPtr<int> p;
try{
p = new int;
catch (bad_alloc exc)X
cout << "Allocation failure!\n";
return 1;
3
*p =88;
*p+=1;
cout << "Value at p is:* << *p << endl;
GCPtr<int> p1 =p;
cout << "p's list size;: " << p.gclistSize() << endl;
p.showlist(0;
GCPtr<int, 10> pA = new int[10];
Iter<int> it = pA.beginO;
int index = 1;
for ( it != pA.end); it++)
7it = index++;

在这里插入图片描述
指针指向对象内含ref引用计数,但使用一个列表记录指针,结束时释放所有列表上的指针以此来解决循环引用问题
在这里插入图片描述

容器适配器(adapter)

stack 堆栈:
一种”先进后出”的容器,底层数据结构是使用的deque;
queue 队列:
一种”先进先出”的容器,底层数据结构是使用的deque;
priority_queue 优先队列:
一种特殊的队列,它能够在队列中进行排序(堆排序),底层实现结构是vector或deque;

优先队列

priority_queue<int> a;//默认大根堆,升序排列
priority_queue<int, vector<int>, greater<int>> b;//升序排列大根堆
priority_queue<int, vector<int>, less<int>> c;//降序排列小根堆

greater和less是仿函数,就是一个类中重载operator()的实现,类就有了类似函数的行为即仿函数,当涉及自定义类型当然可以自己重载;

struct cmp{
	bool operator(type a, type b){
		return a.x < b.x;//大顶堆
	}
}

空间配置器(allocator)

  • 《STL源码剖析》侯捷,SGI STL版本的可读性较强;
  • 从使用的角度来看,allocator隐藏在其他组件中默默工作,不需要关心,但是从理解STL实现角度来看,它是需要首先分析的组件;
  • allocator的分析可以体现C++在性能和资源管理上优化思想;

STL总结
STL的六大组件给软件编程带来了新的多态和复用,是现代C++语言高效的精髓;
泛型和STL的学习路线很陡,建议初学者先学会基本使用和简单扩展;
掌握了一定基础的情况下,可以通过进一步学习和分析源码,编写自己的组件来提升能力;

其他库

boost库
Boost库是为C++语言标准库提供扩展的一些C++程序库的总称,由Boost社区组织开发、维护,Boost库可以与C++标准库完美共同工作,并且为其提供扩展功能;C++标准库也逐渐吸收了其部分的功能;
Boost可为大致为20多个分类:字符串和文本处理库,容器库,算法库,函数对象和高阶编程库,综合类库等等;
官网:https://www.boost.org/
镜像:https://dl.bintray.com/boostorg/release/

基于C++14/17的Http应用框架Drogon(《权力的游戏》中的一条龙的名字(卓耿)): https://github.com/drogonframework/drogon/blob/master/README.zh-CN.md

grpc翻译的中文文档:https://doc.oschina.net/grpc?t=58008 (跨平台 的RPC框架),解释:https://juejin.cn/post/7047885453336248327

腾讯开源的rpc框架phxrpc:https://gitee.com/mirrors/PhxRPC#https://gitee.com/link?target=https%3A%2F%2Fgithub.com%2FTencent%2Fphxrpc%2Fwiki
https://github.com/Tencent/phxrpc

四、C++设计模式

根据前人的经验对一些普遍存在的问题提供的通用的解决方案在软件中有常见的23种面向对象可复用的设计模式。
设计模式也是有代价和适用场景的不是万能的,这23种实际上适合那种扩展性比较强将来会在大量场景中不断重复的使用并且做一些变化的场景中使用;

单例(singleton)模式

在这里插入图片描述

观察者(Observer)模式

在这里插入图片描述
实现思路:
将问题的职责解耦和,将Observable和observer抽象开,分清抽象和实体;

class Observer
public:
	Observer();
	virtual ~Observer();
	//当被观察对象发生变化时,通知被观察者调用这个方法
	virtual void Update(void* pArg) = 0;
}

class User1:public Observer{
	virtual void Update(void* pArg)
	cout <<"User1 Got News:" << endl;
}

class User2 :public Observer{
	virtual void Update(void* pArg)
	cout <<"User2 Got News:" << endl;
}

void,NULL和nullptr是啥?
void*是一个通用指针类型,可以指向任意类型的数据。它不直接关联到特定的数据类型,因此无法进行解引用操作,需要进行显式的类型转换才能使用。例如:

void* ptr = nullptr; // ptr是一个空指针
int* intPtr = static_cast<int*>(ptr); // 将void指针转换为int指针
在较旧的C++版本中,NULL被定义为整数0。这意味着它可以用作指针的空值。然而,使用整数0可能会导致一些歧义,因为0也可以是其他整数类型的值。在现代C++中,推荐使用nullptr代替NULL。例如:

int* ptr = nullptr; // ptr是一个空指针
nullptr是C++11引入的关键字,用于表示空指针。与NULL相比,nullptr具有更好的类型安全性。它可以隐式转换为任意指针类型,并且不能与其他整数类型发生混淆。例如:

int* ptr = nullptr; // ptr是一个空指针

指针是一个变量,用于存储内存地址,可以通过解引用操作符*来访问指向的数据。空指针表示指针不指向任何有效的内存地址。在C++中,使用这些概念可以方便地处理指针为空的情况,避免出现未定义行为或错误。

在这里插入图片描述
23种面向对象设计模式从分类上大致有创建型,结构型和行为型模式;
设计模式不是万能的,它建立在系统变化点上,哪里有变化哪里就可以用;
设计模式为了解耦和,为了扩展,它通常是演变过来的,需要演变才能准确
定位;
设计模式是一种软件设计的方法,不是标准,目前前大部分的框架中都已经包
含了大量设计模式的思想;

泛型编程

在这里插入图片描述

如果说面向对象是一种通过间接层来调用函数,以换取一种抽象,那么泛型编程则是更直接的抽象,它不会因为间接层而损失效率;
不同于面向对象的动态期多态,泛型编程是一种静态期多态,在编译期通过编译器生成最直接的代码;
泛型编程可以将算法与特定类型,结构剥离,尽可能复用代码;

template<class T>
T max(T a, T b){
	return a > b ? a : b;
}

//特化
template<class T1, class T2>
int max(T1 a, T2 b){
	return static_cast<int>(a > b ? a : b);
}

int main(){
	cout << max(1, 3) << endl;
	cout << max(3.5, 1.5) << endl;
	cout << max('a', 'b') << endl;

	cout << max(10, 3.5) << endl;
}

编译器会根据传递给函数的实际参数类型来实例化一个相应的模板函数实例。

泛型与多态、重载的关系:泛型可以提高代码的重用性,但它并不直接涉及到多态或重载的概念。多态是指允许不同类的对象对同一消息做出响应的特性,而重载是指在同一个作用域内,允许有多个同名但参数列表不同的函数。虽然泛型可以提高代码的重用性,但它并不直接影响这些概念。

这个例子演示了泛型,max函数不需要重复的改类型,而是编译器根据传入的参数结合模板生成函数调用,但是如果泛型函数不满足我们的需求的时候,就需要做特化处理.可以打断点调式看运行的是哪一个函数

泛型编程一旦出错,初学者很难纠错!因为是编译器自动完成的很多内容;

函数式编程;

static_cast

在这里插入图片描述

//C++ const转ẽ
const int a = 10;
//int* pA = &a;
int* pA = const_cast<int*>(&a);
*pA = 100:
cout<<a<<endl;可能任然是10,内存模型问题
return O;

在这里插入图片描述

适配器(adapter)模式

复用原代码的功能,不改变然后再做新的功能
多重继承非常不好,和C++有点关系,
菱形继承问题,虚继承

第一种:使用多重继承
第二种:使用组合方式,实例化原类的对象作为新类的成员变量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值