系列目录
指针的本质
编译器将指针的值翻译成一个地址。因此允许用*运算符(后面称为解引用)取用对应内存空间的值。但如果指向的内存空间是非法的,取值会失败。
指针的核心
编译器根据指针的类型确定指针的宽度(++或--后地址的跨度),获取相应宽度内存空间中保存的值。
因此,指针在声明时决定的指针类型并不重要,可以通过类型转换来进行任意地解释。类型转换之后,指针的宽度可能会有所不同,解引用的结果也发生变化。
关于delete关键字的注意事项
delete关键字会释放指针指向的那一片内存,但是指针本身依旧保存着那一块内存的地址。因此,将指针delete之后,需要将其置为空指针。否则,如果后续对该指针进行了误操作,就有可能会造成内存的非法访问,或者重复delete。
int* p = new int(3);
delete p;
p = nullptr; //置空,防止后续误操作
引用的优势
值传递:将对应内存空间的值拷贝一份传入函数,函数内部无法修改对应内存空间。对于类对象,值传递需要调用构造函数和析构函数。
指针传递:将对应内存空间地址传入函数,函数内部可以随意修改内存空间。如果某个操作误修改了指针本身,可能会造成非法访问,不安全。
引用传递:可以看做是安全的指针传递。因为引用类型只能初始化,不能重新赋值。
引用就是编译器维护的一个指针常量。当我们使用引用来访问变量的时候,编译器会自动帮我们进行内存访问(解引用)。
返回值为引用类型
不返回引用类型的情况下,编译器会生成一个临时变量来存储返回值,同时还要经过析构过程。使用引用类型则可以提高效率。一般而言,返回值为引用类型有两种情况:
加const:返回值为右值,只能用来作为操作数。常用于返回类成员的引用。
不加const:返回值为左值,可以被赋值。可用于操作符重载,如:下标操作符[]的重载,可以直接修改对应元素。
不能返回局部变量的引用,因为会被销毁!
不要返回new对象的引用,这种情况下请使用指针!
含义是“静态”,即只初始化一次,具有覆盖整个程序的生命周期;其对应的概念是“非静态”,即动态创建,动态释放。
其作用域仅限于(全局)文件内部或者(局部)对应代码块内部。在作用域之外无法访问,但依旧存在。
static用于修饰局部变量,其会在main函数执行前在数据段,而不是堆栈中分配内存,并在第一次声明时初始化。
static用于修饰全局变量,会取消全局变量的外部性,其他文件将不能访问该全局变量。
static用于修饰成员函数/成员变量,则这个函数/变量不再依赖于类实例,可以用类名来访问。
类的静态成员变量需要在类外单独分配空间(初始化)。
只有静态成员函数能修改静态成员变量。
静态成员函数参数只能修改静态成员变量,因为不会传入this指针作为隐含的参数。
顺序容器
Vector:可变长动态数组,底层实现是数组。支持随机查找,可以在尾部快速地添加或删除元素。
array:数组的上位替代,vector的劣化版。可以忽略
deque:双端队列,底层实现是数组。支持随机查找,可以在两端快速插入或删除元素。
list:双向链表。可以快速插入或删除元素,不支持随机查找。
关联容器
set:集合,底层实现是红黑树,元素按大小排列。查找、插入、删除的复杂度是log(n)。不允许重复元素。
unordered_set:无序集合,底层实现是哈希表,元素不按照大小排列。查找、插入、删除的复杂度是log(1)。不允许重复元素。
map:映射,底层实现是红黑树。查找、插入、删除的复杂度是log(n)。不允许重复元素。
unordered_map:无序映射,底层实现是哈希表,元素不按照大小排列。查找、插入、删除的复杂度是log(1)。不允许重复元素。
容器适配器
stack:栈,底层默认实现是deque。只实现了尾端的push、pop、top操作。
queue:队列,底层默认实现是deque。只实现了队尾push/emplace、队头pop和front操作。
priority_queue:优先队列,底层默认实现是vector和最小堆。实现了pop、push、top方法。
//声明,可以指定底层实现和比较方法
//比较方法默认是less<int>最大堆,greater<int>则是最小堆,也可以自定义
/*自定义比较器,其本质是仿函数
struct cmp {
//重载了()方法
bool operator()(int a, int b) {
return a > b;
}
};
*/
priority_queue<int, vector<int>, cmp> p;
其他容器
string:字符串。其实不是容器,但是支持许多容器操作。
如:append(string)追加操作、substr(string)子串操作、compare(string)比较操作
构造函数
参数情况:无参数;int size;int size, T 元素;同类容器引用;begin, end迭代器。
增加函数
void push_back(const T& x);
void push_front(const T& x);
void emplace_back(const T& x);
iterator insert(iterator it,const T& x);
iterator insert(iterator it,const_iterator first,const_iterator last);//将后两个指针指向的范围的值赋值给前一个指针
删除函数
void pop_back();
void pop_front();
iterator erase(iterator it);
iterator erase(iterator first,iterator last);
void clear();//清空
查找和遍历
T& at(int pos);//需要支持下标的随机访问
T& front();
T& back();
iterator begin();
iterator end();
reverse_iterator rbegin();
reverse_iterator rend();
iterator find(T& x);//查找对应值,没找到返回end()
元素数量
bool empty();
int size();
int count(const_iterator first, const_iterator last, T x);//计算x数量
修改元素
replace(const_iterator first, const_iterator last, T old, T new);//在特定范围内进行替换
void assign(int num, T x);
其他常用操作
void sort(const_iterator first, const_iterator last);
void reverse();//倒序排列
int accumulate(iterator first,iterator last, int sum);//累加
iterator unique(iterator first,iterator last);//去重,实现方法是将后面的元素赋值给前面,尾部的元素仍然保留
利用全局变量和构造函数的特性,通过全局变量的构造函数执行。
在main函数前面定义一个全局变量,全局变量会在main函数执行之前初始化,初始化的时候会自动执行构造函数。
如何在main()函数之后执行一条语句?
同理,全局对象的析构函数会在main函数之后执行。
详见本博客中《设计模式》一文。
class CSingleton {
// 其它成员
public:
//提供一个public的静态函数,用这个函数获取实例
static CSingleton * GetInstance() {
if (m_pInstance == NULL)//如果没有实例化,进行实例化
m_pInstance = new CSingleton();
return m_pInstance;
}
private:
CSingleton(){};//将构造函数标记为private
static CSingleton * m_pInstance;//提供一个成员指针
}
malloc/free
malloc/free为C的标准库函数
void* malloc(size_t size)//参数代表字节个数
void free(void* pointer)//参数代表内存地址
malloc 开辟空间类型大小需手动计算
malloc 返回类型是 void*,必须强制类型转换对应类型指针
malloc/free 函数只是开辟空间并释放,不会调用构造和析构函数
new/delete
new
delete
对于一般的基本类型成员变量来说,使用初始化列表和在函数体里面赋值并没有区别,但下列三种情况有所差异:
常量成员变量必须用初始化列表,因为无法在函数体里赋值。
引用类型成员变量必须使用初始化列表,因为无法在函数体里赋值(引用本质上是指针常量)。
类类型成员变量,如果在函数体中赋值,需要先调用默认构造函数产生一个空的类实例,再通过拷贝构造函数传给成员变量;而使用初始化列表,可以跳过默认构造函数这一步。此外,没有默认构造函数的类必须使用初始化列表。
成员是按照他们在类中定义的顺序进行初始化的,而不是按照在初始化列表中的顺序初始化。这就要求,如果要使用一个成员变量给另一个成员变量初始化,就会出现问题。
class A {
public:
int i ;int j;
A(int x):j(x), i(j){}// 此处i值会先于j值初始化,此时j未定义,导致i未定义
};
逻辑:
效率:
安全:
其他
评论区