开发环境
-
使用MFC或WPF
-
使用跨平台的QT
-
使用图形库OpenGL
-
EasyX 头文件 #include<graphics.h>
基础操作
- 设置背景颜色 setbkcolor(RBG( , , ,))
R=红色 G=绿色 B=黑色
-
initgraph( , ) 初始化图形界面
-
设置画笔样式 : setlinestyle(PS_SOLID,10); <实线,10(宽度)>
-
设置字体颜色 :settextcolor(RBG());
-
设置字体样式 :settextstyle(高度(像素),0(不指定宽度),”微软雅黑“(字体名称))
-
在指定位置输出文本: outtextxy(170,190,“无码”);
在坐标(170,190)的位置打印出无码
-
画线:line(180,380,460,100); 在两个端点之间画
-
输出文本drawtext(“内容”,&rec,DT_CENTER(水平居中)|DT_VCENTER(垂直居中)|DT_SINGLEINE(单行))
-
解决初始化界面图片出现闪烁的问题
-
beingBatchDraw();
加载图片
EndBatchDaraw();
-
图像处理
-
加载图片loadimage(0,“bj.jpg”); 前一个参数为0,可直接加载到界面
-
画方框:rectangle(左上角坐标,右下角坐标)
-
使用背景音乐
需包含 #pragma comment(lib,“winmm.lib”) <告诉编译器,加载winmm.lib 库头文件>
#include<mmsystem.h> //mci 库文件(播放音乐头文件)
Deom: micsendstring(“play 极乐净土.MP3 repeat”,0,0,0); repeat 循环播放
-
IMAGE; //图片类
-
putimage(0,0,images[i]); //从左上角输入图片
语法部分
-
unsigned 无符号型
-
16进制 ox做前缀
-
8进制 o做前缀
-
清空输入缓冲区 cin*.*sync(); VS2015以上版本无效
-
string addr ; addr*.*empty() ; “判断字符串是否为空,返回值 true 、false”
-
fflush(stdio); C语言 清空输入缓冲区
-
更加强大的清空缓冲区方式
1.cin.clear(); //清除错误标记 2.cin.ignore(10,‘1’); //遇到‘1’为止,包含‘1’ 10是最多清楚多少个数据 ios流的最大值:std::numeric_limits<streamsize>::max()
-
-
sprintf: 把指定的信息打印到字符串
demo: sprintf(fileName,“年龄%d”,age); fileName是字符串数组
-
Sleep() //延时 (毫秒)
-
命令提示窗口(改变)
deom: mode con cols(窗口 宽)=40 lines(高)=5
-
C++字符串转C语言字符串 char strl[]; strl.c_str();
-
inline 表示内联函数 inline int add(int a, int b);
(C++流)文件流
Demo:
#include //包含头文件
string fileName;
ifstream file; // 读文件
cin>>fileName;
file.open(fileName);
file>>name>>age; //从文件中读取
-
if(file.fail()){
cerr<<“打开文件出错”<<strerror(errno)<<endl;
//错误原因保存到(errno)
}
- if(file.eof()) //如果读取到文件尾部 eof文件末尾指示器
-
ifstream 对文件输入(读文件)
-
ofstream 对文件输入(写文件)
-
fstream 对文件输入或者输出
-
读写文件格式 : ios:binary (二进制的方式)
-
ios::out|ios::binary (以二进制的方式写入文件)
-
使用文件流对象 write 方法写入二进制数据, read 方法读取二进制数据
- write(char *buffer(起始位置),int count(大小) );
-
格式化读写文件:
`
stringstraeam s;
s<<“姓名:”<<name;
outfile<<s.str(); //outfile 是文件对象
string line;
getline(infile,line); //一行读取
`
-
文件状态检查:
`
file.is_open(); //判断文件是否打开成功
file.eof(); //流file 是否结束
s.clear(); //流S的所有状态都被复位
`
-
文件流的定位:
- .seekg(off_type offset(偏移量), ios::seekdir origin(起始位置));
设置输入流文件的位置
-
.beg (相对于开始位置) .cur (相对于当前位置) .end (相对于结束位置)
-
.tellg //返回该输入流的当前位置(距离文件起始位置的偏移量)
·
s.seekg(0,s.end);
return s.tellg();
·
-
.seekp //设置该输入流的位置
·
s.seekp(4,s.beg);
·
-
设置输入格式:
#include<iomanip> //包含头文件 set(3)<<setiosflags(left) //left左对齐 //控制输出格式(对齐用) 3 表示输出的宽度
指针
-
指针数组 int *k[2];
-
指针强制内型转换
Deom: int arr[]={1,2,3};
void *p=arr;
char *p1=(char *)p;
3.函数指针: 定义int compare_int (void *a,void *b){…}
Demo:
int compare_int(void *,void *); //可以不这样写 void *a
int (*p)(void * ,void *); //把函数名改成( *函数指针名)
fp=&compara_int ; //进行赋值
( *fp)(&x,&y); //或者fp(&x,&y);
4.指针的引用:int *p=&x;
- 定义:int *&q=p;
创建头文件(库头文件)
-
#ifndef _TOOL_H(头文件名 tool.h)
#define _TOOL_H
…
#endif
动态内存分配
-
Dmeo:
int *salary=NULL;
salary=new int[num]; //类型是int 分配数量num个的 salary
释放: delete[] salary;
- salary =new int (1) //只分配一个int大小的空间,并赋初值为1
内存拷贝函数
-
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置。
void *memcpy(void *dest,const void *src,size_t n);
变量存储类型
-
extern 把全局变量在其他的源文件中声明成extern变量,其他的源文件也可以使用这个变量
内存检测工具
-
第一步:包含以下头文件
#define _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<crtdbg.h>
-
接管new操作符
#ifdef _DEBUG
#ifdef DBG_NEW
#define DBG_NE new(NORMAL_BLOCK_FILE,_LINE)
#define new DBG_NEW
#endif
#endif
-
在代码结束输出内存泄漏信息
CrtDumpMemoryLeaks();
获取热键
-
#include<conio.h>
bool quit=false;
do{
if(_kbhit()){ //_kbhit()如果有敲击键盘,则返回true
}
}while(quit==false)
类
|--------------|
| public 方法1 |
| public 方法2 |
|————-------|
| private方法1 |
| private 数据1 | //private方法或者数据都是为实现外部方法而设计的
| private 数据2 |
|----------------|
-
定义一个类:
calss Human{
public: //公有的,对外的
void eat(); //成员函数
string getName();
private: //私有的
string name;
}
-
把函数体写到 Human.cpp 文件中
string Human::getName(){
return name;
}
-
对象的使用:
int main(void){
Human human; //通过特殊的数据类型(Human类)来创建一个对象
法1. human.eat();
法2. Human *p;
p=&human;
p->eat();
}
构造函数
1. 构造函数:对对象内部数据成员进行初始化
-
特点:自动调用、函数名和类名相同、没有返回类型、可以有多个构造函数
Human ::Human(string name,int age){
this->name=name; //实现(函数体)
this->age=age;
}
2. 拷贝构造函数:
-
定义:Human(const Human &other);
-
实现:Human::Human(const Human &other){
name=other.name;
age=other.age;
}
-
补充:const 加在函数的后面,表示不会去修改成员数据
void getName(const string name)const ;
3. 赋值构造函数:
-
定义:Human & operator =(const Human &other);
-
实现:Human & Human::operator=(const Human & other){
if(this==&other){ //防止对象给自己赋值
return *this;
}
name=other.name;
age=other.age;
}
4. 析构函数: 对象销毁前,做清理工作
-
定义: ~Human(); //自动调用
-
实现: Human::~Human(){
delete addr; //addr 分配的内存空间
}
6. 补充: this指针: 特殊的指针,指向对象
类的静态成员函数(static)
-
定义:static int getCount();
-
初始化:static int humanCount;
int Human ::humanCount=0;
-
通过类名直接调用(与具体的对象无关): Human::getCount();
-
只能访问同一类型的对象,不可以访问普通类型的成员
初始化列表
-
const 类型
const int age;
通过初始化列表进行初始化 Human::Human():age(18){
name=“无名氏”;
}
建模手段
**1. ** 组合、聚合
- 组合:同生共死
2. 聚合: (用指针)
-
Cpu *cpu;
//加一个接口,把指针传进去
void add(Cpu *cpu);
Vector 顺序容器
**1. ** stringstream ret; //往里面塞各种东西,都转换成字符串
ret<<name<<“女–颜值”<<yanzhi<<“-年龄-”<<age;
return ret.str(); //转换成C++字符串再返回
**2. ** vectordata; //相当于 int data[];
3. vector<vector>data; //相当于 int data【】【】
4. 尾部添加 data.push_back(1); //相当于数组的的一个成员是1;
5.vector::iterator it =monster.begin();
it!=monster.end(); //it(迭代器)是一种特殊的指针,指向monster的第一个成员
6.求容器的大小(不在分配内容的前提下,容器最大可以包含数据的个数)
vector<int>v1(10);
v1.capacity(); //求容器的大小(10)
赋值: v1.assign(2,88); //修改容器中数据的个数和值
7.重新调整Vector 的大小
1. v1.resize(2); //截取后面的数据,重新定义大小
2. v1.resize(13,888); //重新定义大小,扩大时指定新增的值
8.尾部删除元素: v1.pop_back();
9.访问第一个元素的值: v1.front();
10.插入: 插入一个元素 v1.insert(vi.begin(),888); //在第一个元素的位置插入888
插入多个元素: v1.insert(v1.begin(),3,888);
//在第一个元素的位置插入三个888
11.删除 把整个vector都干掉 v1.clear();
干掉单个元素 v1.erase(v1.begin());
//干掉第一个元素
干掉多个元素: v1.erase(v1.begin(),v1.begin()+3);
继承和派生
-
公有继承:class Son :public Father{ //构造函数和析构函数不能继承,权限访问不变
public:
. string getName();
private:
string name;
}
//先调父类构造函数,再调子类构造函数
-
Son ::Son(const char *name,int age , string game):Father(name,age){…}
-
protected 访问权限(希望被自己的子类访问吗,但是不能被外部访问)
-
私有继承(private):父类中的函数被继承后,访问权限都变成private,
class Son:private Father{…}
-
保护继承(protected ):只把public 降为protected, 其他的不变
-
子类的构造函数: 没有指定的构造函数,就会自动调用父类的构造函数
…
Son (const char * name,const char *game){
this->game=game;
this->name=name;
}
…
-
子类型的作用: 形参作基类交换对象时,实参可以是派生的对象
…demo:
void k(B b){…} ;
A a; //子类
B b; //父类
k(a);
-
多重继承关系的构造函数: D(形参列表):A(实参列表),B(是参列表),C(是参列表){
//其他操作
}
友元
-
两种形式
有元函数(friend void getName())、友元类(frind class Boy) friend void updata(Human *human);
-
友元类(声明): friend void Human::updata(Human *human);
-
注意:1 个头文件中,不能再包含1个头文件 (Boy.h 中 不要包含 Gril.h)
运算符的重载
-
+重载
class Cow class Goat class Pork //三个类 Pork opreator+(const Goat goat);
-
输出运算符的重载
输出:ostream & opreator<<(ostream & os)const ;
-
类型运算符的重载,不需要返回类型
operator int ()const;
多态
-
虚函数表: virtual
virtual void getName();
- 单个的
- VS的对象内存分析:项目命令行配置中添加:/dl reportSingleClassLayout Father(类名)
-
使用继承的虚函数:
- 直接复制父类的虚函数表
- 如果子类重写了父类的某个虚函数,那么就在这个虚函数表中进行相应的替换
- 如果子类增加了新的虚函数,就把这个函数增加到虚函数表中(末尾添加)
-
多重继承的虚函数表:
-
进阶:final 用来修饰类,让类不能被继承
-
class XiaoMi final{
…
}
-
-
override 告诉程序员重写父类的虚函数(仅能用于修饰虚函数)
class Father{ public : virtual void func(); } class Son:public Father{ void func()override{}; }
-
习惯:最好在基类的虚构函数前加virtual
纯虚函数和抽象类
virtual void eat()=0; //用法
核心模块(泛性编程)
-
template<typename T> //template 关键字告诉C++编译器,我要开始泛性编程了, //T- 参数化数据类型 T MAX(T a,T,b){ return a>b?a:b; }
-
模板函数和普通函数
- 当模板函数和普通函数都匹配时,优先调用普通函数.
-
模板类: Son son //类模板必须显示类型
-
类模板和继承
1. 当父类是模板类时,子类是一般类,父类必须在子类里初始化父类的类型 class B:public A<int>{ ...... }
-
类模和友元函数
类里面声明: template<typename T> frind void getName(Son<T> &son);
异常处理机制
int man(){
if(fp==NULL){
throw -1;
}
}
int main(void){
int ret=0;
try{
ret=man();
}catch(int error){
printf("出现错误%d",error);
}
return 0;
}
-
异常处理接口声明:可以在函数声明中列出可能抛出的异常类型,加强程序的可读性.
int max() throw(int ,string *,float){
//…
}
-
throw 类对象异常处理:
-
使用引用类型捕捉,抛出匿名对象
class Student{}; 抛出: throw Student(); 捕捉:catch(Student &error){ //----- }
-
如果是动态分配的对象,直接抛其指针
throw Student(); 捕捉:catch(Student *error){ //----- }
-
-
注意:引用和普通形参传值不能共存.
-
继承和异常(多态vritual)
STL(标准模板库)
- 算法
- 容器
- 迭代器
deque 容器
-
带参构造
//给定区间 deque<int>d1(d2.begin(),d2.end()); //将区间[beg,end)中的元素拷贝给本身 deque<int>d1(10,0); //指定n个元素进行拷贝 deque<int>d1(d2); //拷贝构照
-
在头部添加: d1.push_front(2);
-
删除第一个元素: d1.pop_front();
-
交换元素: d1.swap(d2);
list容器(双向链表)
- 拷贝构造函数 list(const list &lis);
- list的删除: list.remove(elem); //删除容器中所有与elem匹配的值
- 反序输出: listA.reverse();
C++11新特性(完美转发和变参模板)
// 大大提高效率
Sthudent stu;
vector<Student> A;
A.emplace_back(18,"吴献丰"); //增加效率,避免拷贝问题 相当于.push_back();
A.emplace(A.begin(),19,"罗佳荟"); //相当于.insert()
set和multiset 容器
-
set 不允许插入相同的元素
-
musltiset 可以插入相同的元素
-
仿函数(函数对象)function
-
set的插入和pair的用法
pair<set<int>::iterator,bool> ret=setInt.inser(5);树 cout<<*(ret.first)<<endl; cout<<ret.second<<endl;
5.查找
set.find(k); //查找k元素,返回指向k的迭代器 set.count(k); //返回容器中k的元素个数 set.lower_bound(k); //返回第一个>=k的元素的迭代器 set.upper_bound(k); //返回第一个>k的元素的迭代器 set.equal_range(2); //返回容器中与2相等的上下限的两个迭代器 1 2(起) 3(终) 4 pair<set<int>::iterator,set<int>::iterator> it=set.equal_range(k);
Map和multimap 容器
-
关联式容器,键值存储(key,value)
-
区别: map支持唯一的键值,每个键只能出现一次
multimap中相同的键可以出现多次,不支持[]操作
-
map<int,string>mapStu; //插入 mapStu.insert(pair<int,string>(1,"吴献丰")); //使用make_pair mapStu.insert(make_pair(2,"张山")); //使用value_type mapStu.insert(map<int,string>::value_type(3,"王二")); //使用[] mapStu[4]="李四";
queue 容器(队列容器)
priority_queue (优先级队列)
stack容器 (堆栈容器)
- 基于deque实现的
STL的疑难杂症
-
任何时候在模板(template)中使用一个嵌套的从属类型名称时,需要再前面加关键字 typename
template<typename T> void ptint(const list<T>& student) { string line(50,'-'); typename list<Student>::const_iterator it = student.begin(); while (it != student.end()) { cout << *it << endl; it++; } cout << "student.size():" << student.size() << endl; cout << line << endl; }
C++11
1- explicit(关键字),明确的,清楚地 explicit student(int age);
作用:表明构造函数是显示的,而非隐式转换
student x(18); //显示构造
student x=18; //隐式构造
student x={18,"王小美"}; //隐式构造
2- 左值和右值的概念
3- array (容器) 代替C语言中的数组
-
特点: 大小固定,无法动态的扩展和收缩
array<int,4>k; //构造 int类型,大小4 k.fiil(666); //用特定的值填充array中的所有元素
4- 类型转换
int *addr=(int *)OX8888;//强制类型转化,整数直接变成指针
static_cast: 静态类型转化, 如将int 转成char
int i=staic_case<int>(PI);
-
reinterpret_cast :动态类型转换,重新解释,不同类型间的转换,数值与指针的互转
用法:TYPE b=reinterpret_cast<TYPE>(a); TYPE 必须是指针\引用\算数类型\函数指针
-
dynamic_cast 动态类型转换
-
const_cast; 去const属性(仅针对指针和引用)
-
char p[]="123456"; char *p1=const_cast<char *>(p); p[0]='A'; 注意:1 常量字符串不能去const属性 2 在去常量限定时,要保证指针所指向的内存能够被修改,不能修改会引发异常
5- 智能指针
- 原理:将动态分配的内存交给一个有生命周期的对象,来解决指针自动释放的问题.
-
auto_ptr //在C++11后被抛弃 不支持数组
用法:#include<memory> auto_ptr<类型>变量名(new 类型) demo: auto_ptr<string> str(new string("w")); auto_ptr<vector<int>>av(new vector<int>10)); - Tset *temp=str.release(); //取消指针动态内存管理,之前的内存需要手动释放 - delete temp; -str.reset(t1); //改变智能指针的指向,将原来的置空,重新指向t1的地址,什么都不传,默认空指针,将原来的地址置空.
2.unique_ptr - 支持对象数组管理
class D{ //删除器,重载()
public:
void operator()(T *pt){
delete pt;
}
}
unique_ptr<int[]> ui(new int[10]); //会自动调用delete[]函数释放
unique_ptr<T[],D>p(new T[]); //接收D类型的删除器,来释放
3.陷阱: 赋值一定要使用移动语句: p1=move(p2);
-
shared_ptr 一对多(共享指针)
shared_ptr<T> sp; //空的shared_ptr 可以指向类型为T的对象' shared_ptr<T>sp5(new T(),D()); //接受一个D类型的删除器,用D删除器来释放 sp.use_count(); //显示接管T的指针的数目 make_shared //初始化对象(建议使用),分配内存效率高,返回指向对象的shared_ptr 用法: shared_ptr<string>p4=make_shared<string>("字符窜"); 重置: up.reset(); //将up指针置为空指针,所管理的对象引用计数-1 up.reset(p1); //将up重置为p1的值, p1是需要管理的指针 demo: shared_ptr<Person>sp10=make_shared<Person>(); Person *p10=new Person(10); sp10.reset(p10); 使用陷阱: shared_ptr<Gril> sp_gril(new Gril()); shared_ptr 作为被接管的对象成员时,小心因循环引用造成的无法释放资源 解决方法: weak_ptr (弱指针) ;// 配合shared_ptr use_count不会+1 使用: weak_ptr<gril>wp_gril; //定义一个空的弱指针 weak_ptr<gril>wp_gril(sp_gril); //定义一个共享构造
-
结构体内存对齐
原因:1 平台原因 2 效率原因 时间换空间 总大小为最大数的整数倍 #pragma pack(4);// 告诉编译器按照4个字节对齐结构体
### 补充
判断是否有鼠标点击(左键),有的话返回这个消息 ExMessage msg; peekmessage(&msg &&msg.message==WM_LbuuttDNOWN);
-
一个D类型的删除器,用D删除器来释放
sp.use_count(); //显示接管T的指针的数目
make_shared //初始化对象(建议使用),分配内存效率高,返回指向对象的shared_ptr
用法: shared_ptrp4=make_shared(“字符窜”);
重置: up.reset(); //将up指针置为空指针,所管理的对象引用计数-1
up.reset(p1); //将up重置为p1的值, p1是需要管理的指针
demo:
shared_ptrsp10=make_shared();
Person *p10=new Person(10);
sp10.reset(p10);
使用陷阱:
shared_ptr sp_gril(new Gril());
shared_ptr 作为被接管的对象成员时,小心因循环引用造成的无法释放资源
解决方法: weak_ptr (弱指针) ;// 配合shared_ptr use_count不会+1
使用: weak_ptrwp_gril; //定义一个空的弱指针
weak_ptrwp_gril(sp_gril); //定义一个共享构造
5. 结构体内存对齐
```C++
原因:1 平台原因
2 效率原因 时间换空间
总大小为最大数的整数倍
#pragma pack(4);// 告诉编译器按照4个字节对齐结构体
```
### ### 补充
```C++
判断是否有鼠标点击(左键),有的话返回这个消息
ExMessage msg;
peekmessage(&msg &&msg.message==WM_LbuuttDNOWN);