C++标准库体系结构&参数化模型
C++程序运行原理
链接
一般我们编写的程序都会引用基本的库函数,因此在运行程序之前还要把库函用定义好的目标代码替换,这个过程称为链接。就是把自己写的源代码的目标文件与库函数的目标文件组合起来,生成最终的可执行文件。链接由链接器完成。除了ascii码值表示的文件,其余的都是二进制文件,比如hello中用到了printf函数,这是标准c库的函数,存在于一个名为printf.o的单独编译好的目标文件中,这个文件必须以某种方式合并到我们编译好的的目标文件中。链接器(ld)程序负责处理这种合并,结果得到hello文件,它是可执行目标文件,可以被加载到内存中,由系统执行。
转换完成之后就是执行了。在unix系统中,shell是一个命令行解释器,输出一个提示符,等待用户输入然后执行命令。如果输入的第一个单词不是一个内置的shell命令,shell将其解释为可执行文件,比如输入./hello,它将加载并运行这个文件。hello在屏幕上输出信息,然后终止。shell输出一个提示符,等待下一个输入的命令行。具体的过程为:初始时,shell执行它的指令,等待输入。用户输入字符创“./hello”后,shell将字符逐一读入寄存器,然后存放到存储器中,敲回车键后,shell知道用户结束命令输入。然后shell执行一系列的指令来加载可执行的hello文件,将hello目标文件的代码和数据从磁盘复制到主存,数据包含输出的字符串"HELLO,WORLD\n"。一旦目标文件hello中的代码和数据被加载到主存,处理器开始执行main的机器语言指令,将字符串从主存拷贝到寄存器,并输出到屏幕上。
由于涉及大量的主存,磁盘,寄存器通信,故产生了cache等缓冲提高速度的设备,减少通信阻塞。
为了减少用户的负担,操作系统对计算机硬件资源进行了抽象,产生了进程,线程,虚拟地址等概念。进程是程序的一次执行,是操作系统分配资源的单位,多个进程是可以并发执行的,并发执行实际上每个时刻执行的还是一个进程,只不过进程间切换的速度比较快,给人的感觉是并发执行。操作系统为每个进程保存执行的状态信息,称为上下文,包括pc和寄存器文件当前值,主存内容等等。切换进程时,发生上下文切换。一个进程中可以有多个线程执行单元,每个线程都运行在进程的上下文中,共享同样的代码和数据,由于网络服务器等应用对并行处理的需求越来越大,多线程模型也越来越重要。虚拟地址为每个进程提供了一个假象,即每个进程都在独占主存,每个进程看到的是一致的存储器,称为虚拟地址空间。虚拟地址空间是由大量的准确定义的区构成,linux从低地址到高地址依次为:程序代码和数据;堆;共享库;栈;内核虚拟存储器
C++模板
类模板
函数模板
成员函数模板
成员类模板
模板参数模板
Templates四个条款及其使用
标准库体系结构(源码分析)
容器#
迭代器
分配器
重新绑定
rebind用法及其前后脉络
算法 #
仿函数
适配器
concept
关于字符串和转义字符
字符串:
- 字符长度:/0不包含进字符长度strlen(name),不论多少个/0结果一样,但占的空间sizeof(name)还是要算进去 。
转义字符:
- 用法:
//1.表示字符串里的特殊字符
char stra[]={"\"C++\""};
//2.路径表示
char path[]={"C:\\tu\\text.c"}
//相当于
char path[]={"C:/tu/text.c"}
- 转义字符可用于单个字符’、\t,也可以用三位八进制(0-最大0377)和两位十六进制(0-最大0xff)的数字,即将对应十进制ASC值转换成字符,如\141,十进制为97,是a的asc码值,故表示的是a;在字符串中占一个字节。且这种表示法的数字是有限制的,如八进制里不能出现8、且ASC本身只用一个字节去表示字符,故不管是哪个进制都不能超过255(10)
选择语句
if-else
循环语句
- while(){}//必加{}
int i;
while(i<100){
printf(" ");
i++;
}
- do{}while()
int n;
do
{
printf("%d",n);
n--;
}while(n<10)
- for(; ; )
for(int i=1;i<=10;i++){
printf("%d",n);
}
程序执行过程中的内存资源分配
编译和链接与执行是分开的,如果没有编译完成,那么不存在程序被装在用户存储区,全局变量也没有被装进.data区
数组
函数
&:带&函数形参,使用并接收返回值,而 & 叫做引用符,它是 C++ 的内容(目前多数 C 语言编译器也能使用),它可以引用主函数中 x 的地址,而不在调用的函数栈帧中开辟空间,这样就可以对主函数中的 x 进行修改。
效果类似于使用并接收返回值。
*与&:
//指针传递
void swap(int *a, int *b){
cout<<"形参指针a的地址 = "<< a <<endl;
cout<<"形参指针b的地址 = "<< b <<endl;
int tmp = *a;
*a = *b;
*b = tmp;
}
int main(){
int a = 5;
int b = 10;
cout<<"实参变量a的地址 = "<< &a <<endl;
cout<<"实参变量b的地址 = "<< &b <<endl;
cout<<"实参变量a的值 = "<< a <<endl;
cout<<"实参变量b的值 = "<< b <<endl;
//调用函数,指针传递方式
swap(&a, &b);
cout<<"实参变量a的值 = "<< a <<endl;
cout<<"实参变量b的值 = "<< b <<endl;
getchar();
return 0;
}
//若采用指针传递的方式,我们在函数定义和函数声明时使用 *来修饰形参,表示这个变量是指针类型;在进行函数调用时,使用 & 来修饰实参,表示是将该变量的地址作为参数传入函数。
指针
地址小端存储
系统如何访问指针
结构体:
封装的类型,相对于内置类型的定义,需要用大括号初始化;
//定义
struct Student
{
char id[10];
char name[10];
char sex[5];
int age;
};//以上为结构体设置,相当于声明一种类型,须带分号
//}Stud;与struct Student Stud;等价,若在函数外声明相当于声明了一个全局结构体变量
//使用
struct Student stud={"02434","wxinxi","man",21};
//stu只是一个变量,不是一个对象
//结构体只是属性的集合,默认为公有,而类定义的对象是一个属性与方法的集合;
stud.age;//访问
struct Student *stup=&stud;
(*stup).id;
sp->id;//指针访问成员运算符
文件
1标准文件:标准输出文件stdin:显示器printf(),putchar()
标准输入文件stdout:键盘scanf(),getchar()
标准错误文件:显示器
缓冲区:
cpu从键盘读?从缓冲区读?(CPU直接访问键盘的速度太慢)
读缓冲区:CPU访问到getchar,终止对getchar的访问,转而去处理别的任务,当getchar将数据全部读到缓冲区时,CPU才会回来处理
char ch;
scanf("%c",ch);
ch=getchar();
while(getchar()!="\n");{
sum=sum+1;
}
printf("sum:%d\n",sum);
2.宏常量<stdio.h>
-
EOF:可以输入多行数据,换行为每行的标志,当键盘输入ctrl+c,产生EOF,结束标志产生
面向对象
封装(属性+操作)
继承(对象种类不同,而有继承)
继承(继承了属性和方法)中保护与私有区别:在子类中保护可以访问,私有不能访问
若父类没有默认或缺省构造函数,必须在子类中显式调用父类构造进行初始化
多继承和多层继承
多态
多态四种:(覆盖,基类的指针或引用)
重载多态(函数,运算符)
包含多态(虚函数 virtual)
强制多态(强制类型转换 _cast)
参数多态(模板,传过去参数不同,运用函数不同)
泛型编程(数据跟操作分开,操作可以对多种类型操作)
动态联编和静态联编
动态联编:编译时刻不能确定调用哪个
条件:父子,虚函数,指针或引用
c.f(1);//
c.f();//error ,不能直接调用,要通过基类的指针或者引用
//隐藏规则 :A--子函数同名不同参,基类为虚||同名同参基类不为虚,基类的函数被隐藏,不能直接通过子类对象直接调用基类的同名函数
//重载规则:同域,同名不同参,const
//覆盖规则:父子关系-不同域,同名同参,基类必须为虚
//用函数指针数组--把函数的地址存放在数组里
int fn0(int n){
return 0;
}
int fn1(int n){
static int(*p[n])()={fn0,fn1};
return p[!!n]+(n-1);
}
int main(){
cout<<fn1(100)<<endl;
}
//构造函数求解 用对象数组构建n个对象,构造函数会被调用n次,用到静态变量
class A{
private:
static int n;
static int sum;
public:
A(){
n++;
sum+=n;
}
int getsum(){
return sum;
}
};
int main(){
A a[100];
cout<<a[0].getsum<<endl;
}
//用虚函数求解
class A;
A*Arr[2];
class A{
public:
virtual fun(int n){
return 0;
}
};
class B:public A{
public:
int fun(int n){
return Arr[!!n]->fn(n-1)+n;
}
};
void main(){
A a;
B b;
Arr[0]=&a;
Arr[1]=&b;
cout<<Arr[1]->fun(100)<<endl;
}
//用b[a[i]]++;找票据问题、排序问题
int main(){
int a[15]={1,3,3,4,6,7,7,8,0,0,5,4,3,3,2};
for(int i=0;i<14;i++)b[a[i]]++;
for(int i=0;i<=14;i++){
for(int j=0;j<b[i];j++)
cout<<i<<" ";
cout<<endl;
}
}
纯虚函数:
有纯虚函数类的类叫抽象类,不实例化对象,但可以定义指向抽象类的指针或引用,做家族顶尖,让子类去实现多态 =0的本质是将虚函数表指针指向nullptr
避免父类函数无法实现,而定义了对象,无法调用
Shape *p[4]={A a;B b;C c};
高质量C++编程指南
多继承
class A{//sizeof(A)=4;
virtual void fnA(){};
};
class B{//sizeof(B)=4;
virtual void fnB(){};
};
class C{//sizeof(C)=4;
virtual void fnC(){};
};
class D:public A,public B,public C {//sizeof(D)=12;
virtual void fnD(){};
}
class E:public A(){//sizeof(E)=8
virtual void fnD(){};
};
虽然加到了A部分的下面,但是并没有改变A的类的虚表,只是在D对象上的改动
虚函数小结
- 多态得是同名同参同返回值类型。基类返回基类指针,派生类返回派生类指针,这是被允许的一个例外(协变)
- 虚函数只能用在类中
- 类的静态成员不能作为虚函数,静态成员是类每个对象都有的,而虚函数的实现是根据具体对象的不同而决定的
- 构造函数和拷贝构造函数是设置虚指针的,且不能作为虚函数,析构函数一般是作为虚函数的,实现撤销对象的多态性,合理释放空间,实构造,虚析构
- 在运行时的多态,执行速度慢一些,更追求通用性
- 运行时多态的条件
- virtual 放在声明前,不能放在纯定义前(在.cpp里面)
//析构什么时候生效?//执行 delete对象指针
//派生类析构函数没有执行 为什么 运行时把全部的析构函数的函数名全部改为了destuctor
void test(Persen*p){
delete p;
}
void main(){
test(s);
}
//把父类的析构函数写成虚的
运行时多态原理
虚函数指针表存在于只读数据段
vfptr存储在对象中
RTTI 运行时类型识别 内有typeid运算符,可以返回这个指针或对象的实际类型,
typeid作用是运行时获得变量的类型,可以用于静态类型(编译时获得,如非引用)和动态类型(运行时识别,如引用)
//多态的3个例子
//1.
A::fn (int a=10){cout<<"A:fn a="<<a<<endl;}
B::fn (int b=20){cout<<"B:fn b="<<b<<endl;}
//输出:
//A:fn a=10
//B:fn b=10
//虚函数的默认参数是静态绑定,再派生类中重新定义虚函数时,不重新定义继承而来的缺省参数,基类的默认值覆盖了子类的默认值,默认值是在编译期间使用的,虚表是运行时刻创建的
//2.类内调用函数的运行时多态
//3.虚表在哪里开辟空间
class Parent{
public:
virtual fn(){};
};
int main(){
Parent pa;
printf("虚表:%p\n",*((int*)(&pa)));
//在常量区
}
多态:强制类型转换
动态联编和静态联编
一、填空题()
1.面向对象的特征__继承 封装 多态________________。
3.定义重载函数时,应在__参数列表_____上有所不同。
const mutable ++
- 类是用户定义的类型,具有类类型的变量称作_对象______。
类本身不占大小,只是告诉系统应该给对象开辟多大的空间
类的大小
空:一个字节 非空:静态,对齐,指针
- 当一个成员函数被调用时,该成员函数的___this指针____指向调用它的对象。
接收本对象地址,静态成员函数是没有this指针的
- 拷贝构造函数通过_本类对象的常引用______来初始化创建中的对象。
引用传递:传递一个副本过去
指针传递:要加地址符int *P test(&p)//二级指针
Fun(int*&p2)//&p2是别名
Void Test1(int(&a)[4])//传的是整个数组
Void Test2(int a[1000])//传的是首地址,一般写int *a
Test1(arr);
Test2(arr);
三种形式:(1)用类的一个对象去初始化另一个对象时
(2)当函数的形参是类的对象时(也就是值传递时),如果是引用传递则不会调用
(3)当函数的返回值是类的对象或==引用时
7.在C++中有二种__参数传递_____方式即值传递和____引用___传递。
8.在C++中调用.C的函数需要加(兼容转化)extern”C”和#ifdef cppplusplus_______
二、完成程序题()
1.在下面程序的底画线处填上适当的字句,使该程序执行结果为60。
class base{int X;
public∶
void init (int initX){X=initX; }
int Getnum() {return X+7; } }
void main()
{base test;_
___test.init(53);
cout<<test.Getnum();
}
2.分析下面程序的正确性
void test()
{
const int x = 10;
int* p1 = &x;//error *p时会出错
const int* p2 = &x;//指向常量的指针,通过这个指针不能修改x,p2可变
int* const p3 = &x;//error 声明指针类型为const,p3不可变,指针指向的值为int型*p时出错
const int* const p3 = &x;
int num[x] = { 1,2,3,4,5};//ok
char str[]{ “helloworld” };//ok
int b(10);//ok
int c{ 20 }//ok
auto int aa;//error
auto bb;//bb得有值
auto pp = new auto(10);//???
}
- 分析下面程序
class Test
{
public:
Test(int i = 0, int j) :m_j(j), m_i(m_j) {}
void Print() { cout << m_i << " " << m_j << endl; }
private:
int m_i;
int m_j;
};
int main()
{
Test tst(4, 6);
tst.Print();//随机值 6
Test tst1(5);
Tst1.Print();//随机值 0
}
4.在下列程序的空格处填上适当的字句,使输出为:0,8,5。
# include <iostream.h>
# include <math.h>
class Magic{
double x;
public∶
Magic(double d=0.00)∶x(fabs(d)){} //双精度绝对值
operator+(const Magic&c){return Magic(sqrt(xx+c.xc.x));}
friend ostream &operator<<(ostream & os,Magic c);
};
ostream &operator<<(ostream & os,Magic c){return os<<c.x;}
void main()
{Magic ma;
cout<<ma<<’,’<<Magic(-8)<<’,’<<ma+Magic(-3)+Magic(-4);
}
ma 0
-8 8
Ma±3 ±4 5
三、简答题()
- 简述引用和指针
引用是给变量取一个别名,跟原来的变量名没有区别,指针是用来存放指向变量地址的
- C++的三大特性是什么?请简单描述。
面向对象&面向过程
继承,多态,封装
- 谈谈对this指针的理解。
对象创建时,会伴随生成一个隐含的指针,
- 函数实参怎么传给形参?形参有没有开辟内存?如果形参开辟内存,在哪里开辟的?
函数的返回值怎么返回到调用方函数?函数返回后怎么知道从哪条语句开始继续执行?
调用函数时,会在这个被调函数的栈帧中给形参开辟内存,形参会把实参的值复制到对应形参,函数调用中的返回值是放在一个临时变量中的,这个临时变量可能存在于寄存器中,也可能在栈中预先分配的一段空间中(因为编译器根据函数拥有返回值会预先分配空间),函数返回时,再把临时变量的值拿出来,放到应赋给的值所在的空间中(如果有赋值的话)。
CPU执行程序时,并不知道整个程序的执行步骤是怎样的,完全是“走一步,看一步”。前面我们提到过,CPU都是根据PC中存放的指令地址找到要执行的语句。函数返回时,是“从哪里离开,就回到哪里”。但是当函数要从被调函数中返回时,PC怎么知道调用时是从哪里离开的呢?答案就是——将函数的“返回地址”保存起来。因为在发生函数调用时的PC值是知道的。在主调函数中的函数调用的下一条语句的地址即为当前PC值加1,也就是函数返回时需要的“返回地址”。我们只需将该返回地址保存起来,在被调函数执行完成后,要返回主调函数中时,将返回地址送到PC。这样,程序就可以往下继续执行了。
四、综合应用题()
1.分析下列程序可能的输出结果。)
# include “iostream”
class Test
{private∶
int num;
float fl;
public∶
test( );
int getint( ){return num;}
float getfloat( ){return fl;}
~test( );
};
test∶∶test( );
{cout<<″lnitalizing default″<<endl;
num=0;fl=0.0;
}
test∶∶~test( )
{cout<<″Desdtructor is active″<<endl;}
int main( )
{test array[2];
cout<<array[1].get int ( )<<″ ″<<array[1].getfloat()<<endl;
}
五、编程题()
1.写一个字符串类(String),要求至少要有赋值操作符,copy构造函数, +操作符。
class String{
private:
char*str[];
public:
String(const String &other){
if(otherstring==*this){
return *this;
}
else{
m_string=new char[strlen(other.m_string)+1];
strcpy(m_String,other.m_String);
return *this;
}
}
String& operator+(String &s){
if(otherstring==*this){
return *this;
}
else{
m_string=new char[strlen(otherstring.m_string)+1];
strcpy(m_String,other.m_String);
return *this;
}
s.str=str;}
String& operator=(String &s){
s.str=str;
}
};
#include<iostream>
using namespace std;
class String
{
public:
String(const char *str=NULL); //普通构造函数
String(const String &other); //复制构造函数
~String(void); //析构函数
String & operator=(const String &other);//赋值函数
private:
char *m_String; //私有成员,保存字符串
};
String::String(const char *str)
{
cout<<"普通构造函数"<<endl;
if(str==NULL) //如果str为空,存空字符串
{
m_String=new char[1]; //分配一个字节
*m_String='\0'; //将之赋值为字符串结束符
}
else
{
m_String=new char[strlen(str)+1]; //分配空间容纳str内容
strcpy(m_String,str); //赋值str到私有成员
}
}
String::String(const String &other)
{
cout<<"复制构造函数"<<endl;
m_String=new char[strlen(other.m_String)+1];
strcpy(m_String,other.m_String);
}
String::~String(void)
{
cout<<"析构函数"<<endl;
if(m_String!=NULL) //如果m_String 不为NULL,释放堆内存
{
delete [] m_String; //释放后置为NULL
m_String=NULL;
}
}
String & String::operator =(const String &other)
{
cout<<"赋值函数"<<endl;
if(this==&other) //如果对象与other是同一个对象
{
return *this; //直接返回本身
}
delete [] m_String;
m_String=new char[strlen(other.m_String)+1];
strcpy(m_String,other.m_String);
return *this;
}
int main()
{
String a("hello"); //调用普通构造函数
String b("word"); //调用普通构造函数
String c(a); //调用复制构造函数
c=b; //调用赋值函数
return 0;
}
运算符重载
//#include<iostream>
//
//using namespace std;
#if 0
class A
{
public:
A(int i = 0,int j=0):m_i(i),m_j(j){}
A operator+(const A& s)
{
return A(m_i + s.m_i, m_j + s.m_j);
}
void print()
{
cout << "m_i=" << m_i << "m_j=" << m_j << endl;
}
friend A operator-(const A& a, const A& b);
A& operator++(int) {//前加加
++this->m_i;
++this->m_j;//++m_j//ok
return *this;
}
A operator=() {
}
A operator++() {
return A(m_i++,m_j++);
}
int m_i;
int m_j;
};
A operator-(const A& a, const A& b) {
return(a.m_i - b.m_i, a.m_j - b.m_j);
}
int main() {
A a(10, 20);
A b(20, 30);
(a + b).print();
}
#endif
STL
-
模板库Standard Template Library–泛型编程 :类、函数
-
容器:vector
-
算法:sort(a,a+n,greater()) copy find(a,a+n,5) find_if(a,a+n,greter5) reverse swap() swap_ranges()
count(a,a+5,2) count(a,a+5,less5()) include(a,a+10,b,b+5) merge(a,a+5,b,b+5,c) -
迭代器:相当于指针,对容器操作
-
–仿函数:把一个对象(类有重载的小括号)当作算法的函数参数放进去,是泛型编程的一个例子
string是容器类,容器类有通用算法 查find rfind 比int ret=s.compare(s1)相对应的asc11码值--= : 0 >: 1 < : -1 子串 s2=s.substr(1,4)--1:偏移量 4:个数 插 insert(0,"kkk") 擦
Student*p=new Student//p指向new出来的对象
Student*q=(Student*)malloc(sizeof(Student));
拷贝构造:用旧对象创造新对象Student s2(s1) 浅拷贝 深拷贝
浅拷贝 :只复制指向某个对象的指针,而不复制对象本身,相当于是新建了一个对象,该对象复制了原对象的指针,新旧对象还是共用一个内存块
var obj1 ={
name:'张三',
age:8,
pal:['王五','王六','王七']
}
var obj3 = shallowCopy(obj1)
function shallowCopy (src){
var newObj = {};
for(var prop in src ){
console.log(prop)
if(src.hasOwnProperty(prop)){
newObj[prop] = src[prop]
}
}
return newObj
}
obj3.name = '李四'
obj3.pal[0] = '王麻子'
console.log("obj1", obj1); //{age: 8, name: "张三", pal: ['王麻子', '王六', '王七']}
console.log("obj3", obj3); //{age: 8, name: "李四", pal: ['王麻子', '王六', '王七']}
深拷贝:是新建一个一模一样的对象,该对象与原对象不共享内存,修改新对象也不会影响原对象
var arr = ['jack',25,{hobby:'tennise'}];
let arr1 = JSON.parse(JSON.stringify(arr))
arr1[2].hobby='rose'
arr1[0]='rose'
console.log( arr[2].hobby) //tennise
console.log( arr[0]) //jack
var obj = { //原数据,包含字符串、对象、函数、数组等不同的类型
name:"test",
main:{
a:1,
b:2
},
fn:function(){
},
friends:[1,2,3,[22,33]]
}
function copy(obj){
let newobj = null; //声明一个变量用来储存拷贝之后的内容
//判断数据类型是否是复杂类型,如果是则调用自己,再次循环,如果不是,直接赋值即可,
//由于null不可以循环但类型又是object,所以这个需要对null进行判断
if(typeof(obj) == 'object' && obj !== null){
//声明一个变量用以储存拷贝出来的值,根据参数的具体数据类型声明不同的类型来储存
newobj = obj instanceof Array? [] : {};
//循环obj 中的每一项,如果里面还有复杂数据类型,则直接利用递归再次调用copy函数
for(var i in obj){
newobj[i] = copy(obj[i])
}
}else{
newobj = obj
}
console.log('77',newobj)
return newobj; //函数必须有返回值,否则结构为undefined
}
var obj2 = copy(obj)
obj2.name = '修改成功'
obj2.main.a = 100
console.log(obj)
console.log(obj2)
// 安装lodash
npm i --save lodash
// 引入lodash
var _ = require('lodash');
var obj1 ={
name:'jack',
age:25,
}
let obj2 =_.cloneDeep(obj1)
obj2.name = 'rose'
console.log('obj1',obj1.name) //jack
console.log('obj2',obj2.name) //rose
#include<iostream>
#include<vector>
#include<algorithm>
//用队列实现栈、用栈实现队列、给定序列判断合法出栈顺序(可随出随入)
//1.把待判断序列放进队列 2.把队列中的第一个元素与入栈序列的第一个元素一一比较,相等出栈,不相等继续将入栈序列入栈。若最终所有元素全部出栈,是合法顺序
//n入栈元素个数 qq是待判断队列 ss是判断栈
#include<stack>
#include<queue>
void judgeOrder(int*arr,int n,){
queue<int>qq;
stack<int>ss;
for(int i=0;i<n;i++)qq.push(arr[i]);
for(int i=1;i<=n;i++){
while(!ss.empty()&&ss.top()==qq.front){
qq.pop();
ss.pop();
}
ss.push();
}
if(ss.push)cout<<"Correct Order!"<< endl;
else cout<<"Wrong Order!"<<endl;
}
void main(){
int arr[]={3,4,2,1,5};
int n=sizeof(arr)/sizeof(arr[0]);
judgeorder(arr,n);
}
```
#####
#include<stack>
#include<queue>
#include<deque>
#include<list>//带头结点的双向循环链表
using namespace std;
#if 0
//关联容器 已序群集 set map
//vector对象有字符串成员调用构造时须调用拷贝构造
void main() {
vector<string > v;
vector<vector<string>>vv;
for (int i = 0; i < 3; i++) {
v.push_back("");
v[i].append(5, '*');
}
for (int j = 0;j < 3;j++) cout << v[j] << endl;
cout << endl;
cout << endl;
for (int i = 0; i < 3; i++) {
vv.push_back(vector<string>());
for (int j = 0;j < 4;j++) {
vv[i].push_back("12345");
cout << vv[i][j] << endl;
}
cout << endl;
}
vector<int>v2;
vector<int>::iterator iter;//迭代器定义
for (iter = v2.begin();iter != v2.end();iter++) {
cout << *iter << endl;
}
vector<int>v3;
v3.reserve(10);//--v.capacity最小容量,利用push_back可以扩容
v3.resize(5);//--v.size
}
#endif
#if 0
void main() {
vector<vector<int>>vv;
for (int i = 0; i < 3; i++) {
vv.push_back(vector<int>());
for (int j = 0;j < 4;j++) {
vv[i].push_back(4);
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0;j < 4;j++) {
cout << vv[i][j];
}
cout << endl;
}
}
#endif
#if 0
int Print(int n) {
cout << n;
return n;
}
void main() {
vector<int> vv;
copy(istream_iterator<int>(cin), istream_iterator<int>(), back_insert_iterator<vector<int>>(vv));
sort(vv.begin(), vv.end());
copy(vv.begin(), vv.end(), ostream_iterator<int>(cout," "));
for_each(vv.begin(), vv.end(), Print(4));
cout << endl;
}
#endif
#if 0
template <class T>//类型种类有几个写几个class Tn
T Max(T a, T b)//函数模板
{
return a > b ? a : b;
}
void main()
{
cout << Max(1, 2) << endl;
cout << Max('a', 'b') << endl;//模板函数
}
#endif
#if 0
template <class T>
class A
{
public:
A(T i=0):m_i(i){}
void print() {cout << i << endl;}
private:
int m_i = 0;
};
int main() {
A<int> a(10);
a.print();
}
#endif
设计一个求最小值栈
getmin操作复杂度为O(1)。有push,pop,top。
class Stack{
public:
void Push(int x){
data.push(x);
if(MinStack.empty())MinStack.push(x);
else{
if(x>MinStack.top())x=MinStack.top();
MinStack.push(x);
}
}
void Pop(){
data.pop();
MinStack.pop();
}
void Top();
void GetMin(){
return MinStack.pop;
}
private:
stack<int>data;
stack<int>MinStack;};
带权路径长度相同的路径
class Node{};
Class Tree{};
void findpath(Node*&node, vector<vector<int>> &result,vector<int>&path,int sum,){
if(!node)return;
s=s+node->m_value;
path.push_back(node->m_value);
if(!node->m_left&&!node->m_right&&s==sum)result.push_back(path);
fn(node->m_left,s,path,sum);
fn(node->m_right,s,path,sum);
}
void main(){
vector<int>path;
vector<vector<int>> result;
int s=0;
int sum=14;
Tree t;
}
智能指针
唯一性指针:操作堆区 my_unique_ptr
柔性数组:CDTree 两个类对象共用类的指针
移动实现:move
完美转发:forward
//柔性数组 类的最后一个成员是长度为0的数组
class
线程池
多线程
int i=0;
const int n=100;
std::mutex mtx;
std::condition_variable mcv;
void funa(){
std::unique_lock<std::mutex> lock(mtx);
while(i%3!=0){
mcv.wait();
}
if(i<=n) {
cout<<"funa"<<i<<endl;
i++;
}
}
void funb(){
std::unique_lock<std::mutex> lock(mtx);
while(i%3!=1){
mcv.wait();
}
if(i<=n) {
cout<<"funb"<<i<<endl;
i++;
}
}
void func(){
std::unique_lock<std::mutex> lock(mtx);
while(i%3!=2){
mcv.wait();
}
if(i<=n) {
cout<<"func"<<i<<endl;
i++;
}
}
哲学家就餐
对象池的实现
频繁的系统调用(分配内存,创建进程或线程)导致程序频繁地从用户态转换到内核态,非常耗时,为提升程序性能,使用池化技术。
池化技术:提前保存大量资源,来备用资源或重复使用资源,内存池,(Socket/)线程池,连接池,对象池,
池化技术核心:用一个容器保存各种需要的对象,对连接/线程复用,控制复用数量和时间.