一、指针
1、普通指针:
不管指向什么类型的指针,其本质就是一个整数。
指针的创建:
int* pointer=NULL;
int* a,*b;
获取变量地址:
int a;
int* pointer=&a;
修改指针所指向的变量值:
*pointer=1;
2、智能指针
实现自动化的释放内存。
(1)unique_ptr作用域指针
当超出作用域之后内存便会被释放。不能被复制。
#include <iostream>
#include <memory>
class Entity
{
public:
int x, y;
public:
Entity(int x_value,int y_value)
:x(x_value),y(y_value)
{
std::cout << "create objection" << std::endl;
}
~Entity()
{
std::cout << "destory objection" << std::endl;
}
};
int main()
{
{
std::unique_ptr<Entity> entity=std::make_unique<Entity>(1,1);
}//执行到这里时内存释放
}
(2)shared_ptr计数指针
可以被复制,当作用域内所有引用消失后释放内存。
(3)weak_ptr弱指针
3、函数指针
指向函数的指针,但指针的类型比较奇怪,一般用auto或者typedef来替换。
(1) 使用auto替换
//使用auto
#include <iostream>
void print()
{
std::cout << "hello world" << std::endl;
}
int main()
{
auto function = print;//auto简化类型名
function();
}
(2) 使用typedef替换
//使用typedef
int main()
{
typedef void(*function)();
function a= print;
a();
}
(3) lambda
一种匿名函数,可以使用在有函数指针的地方。
#include <iostream>
#include <vector>
#include <algorithm>
void ForEach(const std::vector<int>& values, void(*func)(int))
{
for (int value : values)
{
func(value);
}
}
void print(int value)
{
std::cout << "value:"<<value << std::endl;
}
int main()
{
std::vector<int> values= {1, 2, 3, 4, 5};
ForEach(values, [](int value) {std::cout << "value:" << value << std::endl; });
std::cin.get();
}
二、引用
引用:可以创建引用“变量”,但必须引用现有的值(不赋初值会报错),这个“变量”并不会分配内存。
int a;
int& b=a;
引用其实就是指针的一种修饰用法,使得代码更加简洁,更像是为变量创建了一个别名,引用就是内容本身。
指针用法:改变函数中参数的值
#include <iostream>
int increase(int* value)
{
(*value)++;
}
int main()
{
int a=1;
increase(&a);
std::cout<<a<<std::endl;
}
引用用法:改变函数中参数的值
#include <iostream>
int increase(int& value)
{
value++;
}
int main()
{
int a=1;
increase(a);
std::cout<<a<<std::endl;
}
三、类和对象&结构体
1、类
包含一系列变量和函数,变量和函数默认是私有的。
class Player
{
public:
int x, y;
void move(int add)
{
x += add;
}
};
创建对象——默认在栈上分配
Player player;
2、可见性
protected:
private:
public:
protected:
在类和子类中可以访问。
private:
只有在类中才能访问(子类中也不能访问,除了friend)。
public:
任意地方都可以访问。
3、初始化成员列表
类似于构造函数,对类中的变量进行初始化——按照变量声明顺序进行初始化。
class Player
{
protected:
int x, y;
public:
Player()
:x(1),y(1)
{
}
void get()
{
std::cout << x << std::endl;
}
};
4、 隐式转化
隐式转化只能进行一次,为了简化代码。
class Entity
{
private:
int x,y;
public:
Entity()
:x(1),y(1)
{
}
Entity(int value_X)
{
x=value_x;
}
}
//创建对象
Entity e=2;
5、构造函数和析构函数
(1)构造函数
初始化对象。
class Player
{
public:
int x, y;
Player()
{
x = 0;
y = 0;
}
Player(int value_x, int value_y)
{
x = value_x;
y = value_y;
}
};
(2)析构函数
在程序结束时销毁对象。
class Player
{
public:
int x, y;
Player()
{
x = 0;
y = 0;
}
Player(int value_x, int value_y)
{
x = value_x;
y = value_y;
}
~Player
{
}
};
6、复制和拷贝构造函数
复制会带来时间消耗,有代价,尽量减少不必要的复制。
(1)类的复制
对于类中的指针成员,进行的是浅复制,只复制了地址。
//代码会报错,因为对象的复制是浅复制,会两次释放相同的内存,导致程序错误。
#include <iostream>
#include <String>
class String
{
private:
char* m_Buffer;
unsigned int m_Size;
public:
String(const char* string)
{
m_Size = strlen(string);
m_Buffer = new char[m_Size+1];
memcpy(m_Buffer,string,m_Size+1);
}
~String()
{
delete[] m_Buffer;
}
friend std::ostream& operator<<(std::ostream& stream, const String& string);
char& operator[](unsigned int index)
{
return m_Buffer[index];
}
};
std::ostream& operator<<(std::ostream& stream, const String& string)
{
stream << string.m_Buffer;
return stream;
}
int main()
{
String string = "xyy";
String second = string;
std::cout << string << std::endl;
}
(2)拷贝构造函数
深度地复制,相当于创建一个新对象,对于指针成员会单独分配地址。
#include <iostream>
#include <String>
class String
{
private:
char* m_Buffer;
unsigned int m_Size;
public:
String(const char* string)
{
m_Size = strlen(string);
m_Buffer = new char[m_Size+1];
memcpy(m_Buffer,string,m_Size+1);
}
//拷贝构造函数
String(const String& other)
:m_Size(other.m_Size)
{
m_Buffer = new char[m_Size+1];
memcpy(m_Buffer,other.m_Buffer,m_Size+1);
}
~String()
{
delete[] m_Buffer;
}
friend std::ostream& operator<<(std::ostream& stream, const String& string);
char& operator[](unsigned int index)
{
return m_Buffer[index];
}
};
std::ostream& operator<<(std::ostream& stream, const String& string)
{
stream << string.m_Buffer;
return stream;
}
int main()
{
String string = "xyy";
String second = string;
std::cout << string << std::endl;
}
(3)对象作为函数参数时
当对象作为函数参数传递时会进行一次复制。通常用const type &去传递对象不会复制对象。
//用const引用去传递对象不会带来复制
void print(const String& string)
{
std::cout<<string<<std::endl;
}
7、结构体
包含一系列变量和函数,变量和函数是公有的。
struct operate
{
int x=1, y;
void move(int add)
{
x += add;
}
};
创建结构体:
operate operate_1;
四、关键字
1、static
(1)不同文件中的static:
相当于声明局部变量或函数。
log.cpp中
static int s_value=5;
main.cpp中
int s_vlaue;
(2)类中的static:
类中的static变量作用于所有对象,所有创建的对象共享。
class Player
{
static int x,y;
static int move(int speed)
{
x=x+speed;
}
}
(3)作用域和生命周期 :
static变量的生命周期是整个程序,作用域是声明的函数或者文件。
2、const
(1)修饰常规数据类型
修饰一些常量,相对于给出承诺这些量是不变的——一定要初始化。
const int a=5;
(2)修饰指针
在*号之前——不能修改指针指向的内容。
const int* a= new int;
int const* a= new int;
在*号之后——不能修改指针的值。
int* const a= new int;
(3)修饰类
对类中方法进行修饰——把方法变为只读,不能修改类中数据。
class Entity
{
private:
int a;
public:
int getx() const
{
return a;
}
}
3、mutable
一般用在类中声明的const方法要调用一些参数,可以在变量前加上mutable。
class Player
{
private:
int mutable x, y;
public:
void get() const
{
x = 1;
std::cout << x << std::endl;
}
};
4、new & delete
封装了c语言中的malloc和free,在堆中分配内存。
int* a = new int;
delete a;
int* b= new int[5];
delete[] b;
5、explicit
使得构造函数不能使用隐式转化。
class Entity
{
private:
int x,y;
public:
Entity()
:x(1),y(1)
{
}
explicit Entity(int value_X)
{
x=value_x;
}
}
//创建对象
Entity e(2);
6、this
指向当前对象的指针,不能修改其指针的值。
//用法一---赋值给当前对象
class Entity
{
private:
int x, y;
public:
Entity(int x,int y)
{
this->x = x;
this->y = y;
}
};
//用法二---在函数中传递当前对象作为参数
void printEntity(Entity& e)
{
std::cout<<e.x<<std::endl;
}
class Entity
{
public:
int x, y;
public:
Entity(int x,int y)
{
this->x = x;
this->y = y;
}
printEntity(this);
};
7、inline
内联函数标识符——相当于宏定义。每当程序中出现对该函数的调用时,C++编译器使用函数体中的代码插入到调用该函数的语句之处,同时使用实参代替形参,以便在程序运行时不再进行函数调用。引入内联函数主要是为了消除调用函数时的系统开销,以提高运行速度。
- 内联函数在第一次被调用之前必须进行完整的定义,否则编译器将无法知道应该插入什么代码
- 在内联函数体内一般不能含有复杂的控制语句,如for语句和switch语句等
- 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
#include <iostream>
inline int add(int x, int y)
{
return x + y;
}
int main()
{
std::cout << add(1, 1) << std::endl;
}
8、auto
自动推断变量类型。
auto a = 1;//int
auto a = 1L;//long
auto a = 1.1f;//float
其他简化类型名的方式
using newtype=std::string;
typedef std::string newtype;
9、namespace
命名空间——为了避免命名冲突。
namespace apple {
void print();
}
namespace orange {
void print();
}
五、枚举
枚举:定义一系列的整数。
enum level
{
low,mid,high
};
默认第一个变量为0,后续变量递增。
为了节省内存可以指定enum的类型,不能用float和double,因为enum数为整数。
enum level :char
{
one,two,three
};
六、构造函数&析构函数
1、构造函数
初始化对象。
class Player
{
public:
int x, y;
Player()
{
x = 0;
y = 0;
}
Player(int value_x, int value_y)
{
x = value_x;
y = value_y;
}
};
2、析构函数
在程序结束时销毁对象。
class Player
{
public:
int x, y;
Player()
{
x = 0;
y = 0;
}
Player(int value_x, int value_y)
{
x = value_x;
y = value_y;
}
~Player
{
}
};
七、 继承
继承:实现对其他类的复用。
class object : public Player
{
public:
int a, b;
};
八、虚函数&纯虚函数
1、虚函数
子类对父类方法进行修改。
class Player
{
public:
int x, y;
Player()
{
x = 0;
y = 0;
}
Player(int value_x, int value_y)
{
x = value_x;
y = value_y;
}
virtual void move(int speed)
{
x = x + speed;
y = y + speed;
}
};
class object : public Player
{
public:
void move(int speed) override
{
x = x + 2 * speed;
y = y + 2 * speed;
}
};
2、纯虚函数
在父类中对方法不做定义,提供了类似接口的方式,方便在子类中编写方法——方法必须在子类中实现,同时不能用有纯虚函数的类创建对象。
class Player
{
public:
int x, y;
Player()
{
x = 0;
y = 0;
}
Player(int value_x, int value_y)
{
x = value_x;
y = value_y;
}
virtual void move(int speed)=0;
};
class object : public Player
{
public:
void move(int speed) override
{
x = x + 2 * speed;
y = y + 2 * speed;
}
};
九、数组、字符串
1、数组创建
int a[5];
//---//
#include <array>
std::array<int,5> a;
2、 字符串创建
const char* c = "1234";
//---//
#include <string>
std::string a = "1234";
十、运算符和重载
1、运算符意义
运算符本质就是函数。
c=a+b;
c=a/b;
c=a*b;
c=a\b;
c=a<<b;
2、 ->操作符
简化指针运算符操作。
struct vector2
{
float x;
float y;
};
int main()
{
vector2 v = {1,1};
vector2* e=new vector2();
e = &v;
std::cout<<e->x<<std::endl;
}
3、 重载
在程序中定义或修改运算符的行为(尽量少用,会导致程序难以阅读)。
//定义运算变量形式---向量相加
struct vector2
{
float x, y;
};
//定义函数
vector2 add(vector2 a, vector2 b)
{
vector2 result;
result.x = a.x + b.x;
result.y = a.y + b.y;
return result;
}
//运算符重载
vector2 operator+(vector2 a, vector2 b)
{
return add(a,b);
}
十一、外部库使用
1、静态链接
2、动态链接
十二、模板
让编译器帮你写代码。
1、函数中使用
对函数参数类型进行替换。
//模板只有在调用时才会编译
//相当于做填空进行替换
template<typename T>
print(T value)
{
std::cout<<value<<std::endl;
}
int main()
{
print(5);
print("hello");
}
2、类中使用
指定数组大小
template<int N>
class array
{
private:
int a[N];
public:
int Getsize() const
{
return N;
}
};
int main()
{
array<5> a;//指定N的大小
}
十三、宏定义
十四、多线程
基本用法
#include <iostream>
#include <thread>
static bool s_finished = 0;
void DoWork()
{
std::cout << "Working" << std::endl;
}
int main()
{
std::thread worker(DoWork);
std::cin.get();
s_finished = 1;
worker.join();
std::cin.get();
}
十五、计时
#include <iostream>
#include <chrono>
#include <thread>
int main()
{
using namespace std::literals::chrono_literals;
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(1s);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> duration = end - start;
std::cout << duration.count() << std::endl;
std::cin.get();
}
利用类的特性构造函数和析构函数——作用域结束后调用析构函数,来实现计时。
class Timer
{
std::chrono::time_point<std::chrono::steady_clock> start, end;
std::chrono::duration<float> duration;
Timer()
{
start = std::chrono::high_resolution_clock::now();
}
~Timer()
{
end = std::chrono::high_resolution_clock::now();
duration = end - start;
std::cout << "Timer:" << duration.count() << std::endl;
}
};
十六、函数
1、函数返回多个值
2、函数指针
void helloworld()
{
std::cout << "hello world" << std::endl;
}
int main()
{
/*函数指针用法一*/
void(*function_1)();
function_1 = helloworld;//隐式转化=&helloworld
function_1();
/*函数指针用法二*/
typedef void(*hellofunction)();
hellofunction function_2 = helloworld;
function_2();
std::cin.get();
}