数据类型
char 8位
short int 16位
long int 32位
int
float
double
long double
bool
wchar_t
开发环境
visual studio
标识符
A-Z 或 a-z 或 _开始 + 字母/下划线/数字
不允许数字开头和出现标点字符
区分大小写
关键字
https://en.cppreference.com/w/cpp/keyword
常量与变量
#define // 编译时出错,很难排错
const // 在编译时出错,可以排错
整数常量
前缀指定基数
后缀指定无符号还是长整型
布尔类型
true
false
字符常量
L'x' // 宽字符常量(wchar_t类型)
\是一个转义字符
注释
/*aaa*/
/*
* bbb
*/
运算符表达式和基本语句
算术运算符
+ - * / % ++ --
关系运算符
== != > < >= <=
逻辑运算符
&& || !
赋值运算符
= += -+ *= /= %=
<<= >>= &= ^= |=
位运算符
& 都为1才为1
| 有1就为1
^ 不同为1, 相同为0
<< 左移
>> 右移
杂项运算符
sizeof
Condition ? X:Y
, 逗号运算符会顺序执行一系列运算,整个表达式的值是以逗号分隔的列表中的最后一个表达式的值
. 和 -> 成员运算符用于引用类,结构和共用体的成员
Cast 强制转换运算符把一种数据类型转换为另一种数据类型
& 指针运算符返回变量的地址
* 指针运算符指向一个变量
结构体
typedef struct {
short Sunday;
} Week;
Week week;
week.Sunday = 0;
cout << week.Sunday << endl;
运算符优先级
一般来说,一元运算符优先级高于对应的二元运算符
弄不清优先级,就加括号
补码
1,用函数B2T来表示
2,正数直接按位计算权重和,负数保留符号位,对后面每位取反+1
补码数值范围(字长)
字节序
大端法
小端法
位运算
左移(补零)
右移(逻辑右移填充0或算术右移负数填充1)
对有符号的数,尽可能不要使用右移运算
c语言常见问题及c++的解决方案和思考
c语言字符的语法陷阱
char c1 = 'test';
const char* slash1 = "/";
c++使用string类
string s1(1, 'yes');
cout << s1 << endl;
string s2(3, 'yes');
cout << s2 << endl;
string s3(1, 'y');
cout << s3 << endl;
string s4("/");
cout << s4 << endl;
string s5(1, '/');
cout << s5 << endl;
string s6("yes");
cout << s6 << endl;
c语言指针和数组的关系问题
数组退化的问题
传递数组会退化成指针
c++使用stl的容器及引用的使用
vector<int> & v
auto自动定义类型
c语言的移位问题
右移先转换成无符号的形式
移位操作位数的限制: 移位数大于0,小于位数
c++的bitset使用
bitset<10>
c语言强制类型转换问题
比较时发生了隐式的类型转换
运算时发生了隐式的类型转换
c++使用
static_cast
const_cast
dynamic_cast
reinterpret_cast
c语言的整数溢出问题
整型上限溢出
c++boost库使用
https://www.boost.org
c语言字符串的典型缺陷
c++的redis实现
c++容器的实现
序列型容器-数组
int arr[10]={1,2,3,4,5,6,7,8};
二维数组遍历先行后列有利于提供效率
vector是面向对象方式的动态数组
vec.insert(--vec.end(),4);
vec.erase(vec.end()-1);
字符串变量与常量
字符串是以空字符结束的字符数组
char str[11] = {"helloworld"};
ascii编码
utf-8编码
utf-8
1byte来表示字符,可以兼容ascii码
存储销量高,变长(不方便内部随机访问)
无字节序问题
utf-16
分为utf-16BE(big endian)和utf-16LE(little endian)
定长(方便内部随机访问)
有字节序问题
utf-32
分为utf-32BE(big endian)和utf-32LE(little endian)
定长(方便内部随机访问)
有字节序问题
编码错误的根本原因在于编码方式和解码方式的不一致
字符串的指针表示方法
& 取地址
* 定义指针变量
字符串常见操作
strlen(s) // 返回字符串s的长度
strlen_s(s) // 安全版本
strcmp(s1,s2) // 字符串比较(0,-1,1)
strcpy(s1,s2) // 将字符串s2拷贝到s1中
strcpy_s(s1,s2) // 安全版本
strncpy(s1,s2,n) // 将字符串s2中前n个字符拷贝到s1中
strcat(s1,s2) // 将字符串s2拼接到s1后面
strcat_s(s1,s2) // 安全版本
strchr(s1, ch) // 指向字符串s1中字符ch的第一次出现的位置
strstr(s1, s2) // 指向字符串s1中字符串s2的第一次出现的位置
string
string s;
string s = "helloworld";
string s("helloworld");
string s = string("helloworld");
字符串比较: == != > >= < <=
转换成c风格的字符串
const char* c_str = s1.c_str();
字符串拷贝
string s1 = "hello";
string s2 = s1;
字符串连接: + +=
c++指针/引用
定义指针变量,指向对象的地址,可以获得指向对象的值
左值与右值
左值是编译器为其单独分配了一块存储空间,可以取其地址的,左值可以放在赋值运算符左边
右值是数据本身,不能取到其自身地址,右值只能赋值运算右边
一般指针,指针的数组,数组的指针
int* ap = &a;
T* t[];
T(*t)[];
const指针
char const* == const char* (指向字符)
char* const (指向指针)
char const* const == const char* const (都不允许改变)
指向指针的指针
int a = 123;
int* b = &a;
int** c = &b;
野指针
未初始化和非法的指针
NULL指针
指针操作超越了变量的作用范围
没有初始化,不用的或者超出范围的指针请把值只为NULL
指针的基本运算
取地址操作后会复制到一个新的空间,所以取地址的操作是右值的操作
&与*操作符
++与--操作符
++++与----运算符
++*++cp 等运算
存储区域划分
栈和队列
指针在栈区,指向堆区栈区或者常量区
全局变量可以不用初始化
堆
new
delete
资源管理方案RAII
依托栈和析构函数来对所有的资源包括堆内存在内进行管理
作用域
堆区 由new malloc开始,delete,free结束
智能指针
unique_ptr
shared_ptr
weak_ptr
auto_ptr(已废除)
auto_ptr
由new 表达式获得对象,在auto_ptr对象销毁时,他所管理的对象也会自动被delete掉
多个auto_ptr指向同一个对象,原来的指针就不再拥有这个对象了
unique_ptr
专属所有权
shared_ptr
引用计数(循环引用)
weak_ptr
与shared_ptr共同工作,用一种观察者模式工作
引用(不允许修改的指针)
可以认为是指定变量的别名,使用时可以认为是变量本身
c++的引用
有了指针为什么还需要引用
为了支持函数运算符重载
有了引用为什么还需要指针
为了兼容c语言
对内置基础类型而言,在函数中传递value(值)更高效
对面向对象中自定义类型而言,在函数中传递reference(引用)更高效
c++基础句法
三种基本结构
顺序,分支和循环
if
单分支/语句双分支语句/多分支语句
判断效率
逻辑短路
switch
汇编角度(跳转表),条件多时效率更高
枚举
enum不仅能够创建符号常量,还能定义新的数据类型
枚举值不可以做左值
非枚举变量不可以赋值给枚举变量
枚举变量可以赋值给非枚举变量
enum wT {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
wT weekday;
weekday = Monday;
weekday = Tuesday;
cout << weekday << endl;
int a = Wednesday;
cout << a << endl;
结构体和联合体
union Score{
double sc;
char level;
}
struct Student {
char name[6];
int age;
Score s;
}
结构体的内存布局
定义变量先低后高节省内存(最大元素整数倍对齐)
char x;
short y;
int z;
32位cpu
char: 任何地址
short: 偶数地址
int: 4的整数倍地址
double: 4的整数倍地址
修改默认编译选项:
Visual C++:
#pragma pack(n)
g++:
__attribute__(aligned(n))
__attribute__(__packed__)
三种循环语句: while, do while 和 for
dowhile(效率高) > while > for(简便)
for 循环优化例子
void forOptimize() {
int n = 0;
int high, low;
for (size_t index = 32; ; index++) {
n = index * index;
if (n < 1000) continue;
if (n > 9999) break;
high = n / 100;
low = n % 100;
if ((high / 10 == high % 10) && (low / 10 == low % 10))
cout << n << endl;
}
}
函数
返回类型
函数名称
参数
函数重载
targetver.h 可以屏蔽某些平台
int test(int a) {
return a;
}
int(*p)(int);
p = test;
int result = (*p)(1);
指向函数的指针与返回指针的函数
指向函数入口地址的指针称为函数指针
数据类型(*指针变量名)(参数表)
int(*p)(int); // 可以作为参数进行传递
返回值是指针的函数
int* p(int);
命名空间
.h
namespace testnamespace {
int test(int a);
}
.cpp
namespace testnamespace {
int test(int a) {
return a + 1;
}
}
函数体的hack过程
入栈跳转出栈
内联函数如果一个函数是内联的,那么编译时会把该函数的代码副本放置在每个调用该函数的地方
inline
内联函数内部不能有太复杂的逻辑,编译器有时会有自己的优化策略,所以内联不一定起作用
数学归纳与递归
递归的缺陷
空间上需要开辟大量的栈空间
时间上可能需要有大量重复运算
递归的优化
尾递归
使用循环替代
使用动态规划,空间换时间
尾递归的优化
int Fib1(int n) {
if(n==0) {
return 0;
}else if(n==1) {
return 1;
}else {
return Fib1(n-1) + Fib1(n-2);
}
}
int Fib2(int n, int ret0, int ret1) {
if(n==0) {
return ret0;
}else if(n==1) {
return 1;
}
return Fib2(n-1, ret1, ret0 + ret1);
}
递归的动态规划思路
高级语法
struct的默认成员权限是public
class的默认成员权限是private
构造函数
析构函数
对象属性
操作符重载
Complex::operator+ (const Complex& x);
Complex& operator= (const Complex& x);
拷贝构造及临时对象的优化
拷贝构造
Complex(const Complex& x);
操作符重载对象前置与后置操作符
前置效率高
标准输入输出io重载
friend ostream& operator << (ostream &os, const Complex &x);
os << "aaa" << x._real << "bbb" << x._image;
friend istream& operator >> (istream &is, Complex &x);
is >> x._real >> x._image;
空格或者回车
cin >> e;
cout << e << endl;
io 流基础
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
文件操作基础
文本文件
二进制文件
文件操作步骤
打开文件用于读和写(open)
检查打开是否成功(fail)
读或者写(read/write)
检查是否读完(EOF)
使用完文件后关闭文件(close)
#include <fstream>
```
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
static const int bufferLen = 2048;
// 二进制流复制文件
bool CopyFile(const string &src, const string &dst) {
ifstream in(src.c_str(), ios::in | ios::binary);
ofstream out(dst.c_str(), ios::out | ios::binary | ios::trunc);
if (!in || !out) {
return false;
}
char temp[bufferLen];
while (!in.eof()) {
in.read(temp, bufferLen);
streamsize count = in.gcount();
out.write(temp, count);
}
in.close();
out.close();
return true;
}
int main() {
bool isCopy = CopyFile("/Users/xingyue/Downloads/test.jpeg", "/Users/xingyue/Downloads/test1.jpeg");
cout << isCopy << endl;
return 0;
}
```
头文件的重复包含问题
两种方式
1,
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
使用宏来防止同一个文件被多次包含
可移植性好,无法防止宏名重复,难以排错
2,#pragma once
使用编译器防止同一个文件被多次包含
可以防止宏名重复,易排错
可移植性不好
只考虑windows系统可以用方案2,否则用方案1(注意名字不要重复)
深拷贝浅拷贝及move语义的优化
浅拷贝只拷贝指针地址
深拷贝重新分配堆内存
写时复制
抽象-抽象类型
构造函数使用全局变量参数列表初始化成员变量
多态实现的方法需要加上virtual
抽象方法 virtual double Area() const = 0;
对象模型和虚函数
虚表替换指针实现多态
面向对象特性
封装性
数据和代码捆绑在一起,避免外界干扰和不确定性访问,封装可以使得代码模块化
继承性
让某种类型对象获得另一个类型对象的属性和方法,继承可以扩展已存在的代码
多态性
同一事物表现出不同事物的能力,即向不同对象会产生不同的行为,多态的目的则是为了接口重用
编程思想
单例模式(只有一个实例)
拥有一个私有构造函数,确保用户无法通过new直接实例她
包含一个静态私有成员变量instance与静态公有方法instance
static和单例模式的实现
static const Singleton* getInstance(); // 声明获取单例对象函数
static Singleton* This; // 使用静态变量
观察者模式(发布订阅)
virtual void Update(void* pArg) = 0;
list<Observer*> _Obs;
list的使用和观察者模式的实现
观察者模式的实现
void*NULL和nullptr
c++11中,nullptr用来替代(void*)0,NULL替代0
参数直接传NULL,会变成0
传指针类型的参数就是指针
隐式类型转换
显示类型转换
const_cast
用于转换指针或引用,去掉类型的const属性
reinterpret_cast的使用
重新解释类型,既不检查指向的内容,也不检查指针类型本身,但要求转换前后的类型所占用内存大小一致,否则将引发编译时错误
satic_cast
用于基本类型转换,有继承关系类对象和类指针之间转换,由程序员来确保转换是安全的,它不会产生动态转换的类型安全检查的开销
dynamic_cast
只能用于含有虚函数的类,必须用在多态体系中,用于类层次间的向上和向下转换,向下转化时,如果是非法的对于指针返回NULL
Adapter模式和多重继承
Adapter组合方式实现
设计模式总结
泛型编程之泛型函数
泛型编程是一种静态期多态,通过编译器生成最直接的代码
泛型编程可以将算法与特定类型,结构剥离,尽可能复用代码
泛型编程之泛型函数
泛型编程的递推过程及总结
进阶编程
stl标准库介绍
stl算法是泛型,不与任何特定数据结构和对象绑定,不必在环境类似的情况下重写代码
stl算法可以量身定做,并且具有很高的效率
stl可以进行扩充,你可以编写自己的组件并且能与stl标准的组件进行很好的配合
stl标准库六大组件
空间配置器
容器
适配器
仿函数
算法
迭代器
容器
序列式容器(可排序)
vector,list,deque序列式容器
stack,queue,priority_queue容器适配器
关联式容器(k,v)
set.multiset,map,multimap
仿函数(使用对象的方式操作函数)
仿函数一般不会单独使用,主要是为了搭配stl算法使用
函数指针不能满足stl对抽象性的要求,不能满足软件积木的要求,无法和stl其他组件搭配
本质就是类重载了一个aopertor(),创建一个行为类似函数的对象
模板(泛型)
仿函数
仿函数+模板(泛型)
stl算法(<algorithm>,<numeric>, <functional>)
非可变序列算法
可变序列算法
排序算法
数值算法
最常见的算法包括
查找,排序算法,通用算法,排列组合算法,数值算法,集合算法
二分查找(binary_search)
transform算法
int ones[] = {1,2,3,4,5};
int twos[] = {10,20,30,40,50};
int results[5];
transform(ones, ones + 5, twos, results, std::plus<int>());
lambda表达式
[](int a) -> void {}
stl容器的统计与二分查找
从手写全排序到stl的实现
迭代器基本使用
正向迭代器: iterator
常量正向迭代器: const_iterator
反向迭代器: reverse_iterator
常量反向迭代器: const_reverse_iterator
手写GC与异常
自定义迭代器与手写GC
容器适配器(adapter)
stack堆栈
queue队列
priority queue优先队列
空间配置器初步(allocator)
自定义空间配置器
stl空间配置器源码剖析与stl总结
boost库
字符串和文本处理库,容器库,算法库,函数对象和高阶编程库,综合类库等
c++ 多线程(c++11)
linux c++ 编程
gcc编译需要添加标准库等
gcc -lstdc++ helloword.cpp -o helloword
g++编译
g++ -Wall helloword.cpp -o helloword
gcc命令的编译选项
-shared 生成共享目标文件,通常用在建立共享库时
-static 禁止使用共享连接
-Wall 生成所有警告信息
-IDIRECTORY 指定额外的头文件搜索路径DIRECTORY
-LDIRECTORY 指定额外的函数库搜索路径DIRECTORY
-ILIBRARY 连接时搜索指定的函数库LIBRARY
源程序.cpp -> (预处理器) -> 被修改的源程序.i -> (编译器) -> 汇编程序.s -> (汇编器) -> 可重定位目标程序.o -> (连接器) -> 可执行目标程序helloword
Makefile的使用和编写
描述了整个工程所有文件的编译顺序,编译规则
make工具就根据Makefile中的命令进行编译和链接的
Makefile的格式
TARGET = main
OBJS = reply.o.main.o
.PHONY: clean
$(TARGET):$(OBJS)
g++ $(OBJS) -o $(TARGET)
reply.o: reply.cpp
main.o: main.cpp
clean:
rm $(TARGET) $(OBJS)
install:
cp /usr/local/bin/mainTest
Makefile的扩展用法
make工程的安装和卸载
make install
make uninstall
Makefile的变量
用户自定变量,变量中的变量,追加变量,多行变量,环境变量,自动变量,模式变量,自动匹配
a = $(x) // 可以读取后面初始化的x
a := $(x) // 不能读取到后面初始化的x
多行变量
define xxx
aaa
bbb $(bar)
endef
test:
echo $(xxx)
echo $PATH
echo $LANG
echo $HOME
echo $PWD
echo $LD_LIBRARY_PATH
$(CXX) ==> g++
$(RM) ===> rm -f
Makefile 动态库编写
TARGET = main
OBJS = reply.o
LIB = libreply.so
CXXFLAGS = -c -fPIC
.PHONY: clean
$(TARGET):$(LIB) main.o
$(CXX) main.o -o $(TARGET) -L. -lreply -Wl, -rpath ./
$(LIB):$(OBJS)
$(CXX) -shared $(OBJS) -o $(LIB)
reply.o:reply.cpp
$(CXX) $(CXXFLAGS) reply.cpp -o $(OBJS)
main.o:main.cpp
$(CXX) $(CXXFLAGS) main.cpp -o main.o
clean:
rm $(TARGET) $(OBJS)
make后
g++ -c -fPIC reply.cpp -o reply.o
g++ -shared reply.o -o libreply.so
g++ main.o -o main -L. -lreply -Wl, -rpath ./
自动变量和模式变量
编译过程中产生的变量
$< 表示第一个匹配的依赖
$@ 表示目标
$^ 所有依赖
$? 所有依赖中更新的文件
$+ 所有依赖文件不去重
$(@D) 目标文件路径
$(@F) 目标文件名称
% 表示任意字符
TARGET = main
OBJS = reply.o
LIB = libreply.so
TESTOBJ = main.o
CXXFLAGS = -c -fPIC
LDFLAGS = -L. -lreply -Wl, -rpath $(@D)
SHARED = -shared
.PHONY: clean
$(TARGET):$(TESTOBJ) $(LIB)
$(CXX) $< -o $@ $(LDFLAGS)
$(LIB):$(OBJS)
$(CXX) $(SHARED) $^ -o $@
%.o:%.cpp
$(CXX) $(CXXFLAGS) $^ -o $@
clean:
$(RM) $(TARGET) $(OBJS) $(LIB) $(TESTOBJ)
https://www.gnu.org/software/make/manual/make.html
Makefile的自动生成和部署
https://www.gnu.org/software/automake/manual/automake.html
https://cmake.org/documentation
下载解压cmake-2.8.10.2
cd cmake-2.8.10.2
./bootstrap
gmake
sudo gmake install
cmake --version
编写CMakeLists.txt
#CMakeLists.txt
# 设置cmake最低版本
cmake_minimum_required(VERSION 2.8.0)
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
# 项目名称
project(cmake_test)
# 包含的头文件目录
include_directories(./include)
set(SRC_DIR ./src)
# 指定生成链接库
add_library(XXX ${SRC_DIR}/XXX.cpp)
add_library(YYY ${SRC_DIR}/YYY.cpp)
# 设置变量
set(LIBRARIES XXX YYY)
set(OBJECT YYY_test)
# 生成可执行文件
add_executable(${OBJECT} ${SRC_DIR}/main.cpp)
# 为可执行文件链接目标库
target_link_libraries(${OBJECT} ${LIBRARIES})
https://pan.baidu.com/s/1xtFeXEMNFbeONVX2KN8VQQ
8veq
其他技巧
头文件
- #include "xxx" : 编译器首先在当前目录中查找要包含的文件。如果没有找到,则会在编译器设置的标准文件路径中查找该文件。通常情况下,使用双引号来包含自己编写的头文件
- #include <xxx> : 编译器只在标准文件路径中查找要包含的文件。通常情况下,使用尖括号来包含系统头文件
因此,使用第一种形式#include "xxx",您可以在本地目录和/或用户定义的目录中包含特定的文件。而使用第二种形式 #include <xxx>,您只能在标准目录中包含文件
内存剖析
c/c++内存调试初步
现场分析问题
跟踪错误的根本原因
了解计算机系统底层/软件架构
例子: 相同的值异或会变成0
汇编语言的初步
从汇编语言的视角看程序
剖析一个函数栈的结构
详细分析main函数的执行流程
shellcode基本原理和案例
推荐的书本
C++ primer
C++高质量编程
Effective C++
More Effective C++
Effective STL
The C++ Programming Language
STL源码解析
COM本质论
Exceptional C++
Inside the C++ Object Model
The Design and Evolution of C++
c++开发规范
双刃剑: 内存管理/指针 模板元编程
// ==========桌面应用项目实战========== //
设计项目的架构和标准
通用组件模块
ui模块开发
音频模块开发
项目总结