1. vector clear函数详解:释放元素、释放内存、内部调度过程
直接声明的vector容器是一个普通变量,相当于一个未知大小的动态数组,不需要手动释放,超出作用于范围时会自动回收。
如果vector内存储的是元素而不是指针,这些元素会随vector的析构而自动释放。
如果vector内存储的是指针,在vector析构时,指针指向的由程序员new() / malloc() 分配的nei s
1.1 clean() 部分源码 + vector的影响:
vector.clear()实际作用是将vector容器的size重置为0(size是vector容器当前存储的元素数目),但不会改变vector的容量(capacity表示当前vector在内存中申请的这片区域所能容纳的元素个数),并不会释放程序员自己动态分配的内存。
真正释放内存的地方在vector的析构函数,在跳出某个vector的作用域后,析构函数自动启动,依次调用内部各个元素的析构函数,然后调用allocator中的deallocate释放容器本身的内存。
// clear函数最终调用部分
for ( ; first < last; ++first) //遍历元素进行析构
destroy(&*first);
// destroy() 单指针版本
template <class T>
inline void destroy(T* pointer) {
pointer->~T(); // 唤起 dtor ~T()
}
// 其中first是迭代器
// *iterator 表示元素本身,&*iterator 是一个指针
// 之后唤起每个指针的析构函数
1.2 vector元素是对象(int、float等object)
调用 clear 函数后,自动为当前存储的每个元素调用各自的析构函数,并将size置为0。
void clear() { erase(begin(), end()); }
1.3 vector元素是普通指针
如果存储的是指向对象的指针,则并不会调用相应的析构函数,也就是这些指针指向的对象并不会销毁。
这种情况下如果直接调用clear()函数 或者 vector 销毁,内部元素(指向对象的指针)并不会自动释放,会造成内存泄漏。解释一下,如果需要删除的内部元素——指针,其指向的对象没有其他引用,在调用clear()之后将会失去这部分对象的访问能力,并且这部分对象也不会自动销毁,内存就不会释放!
所以想要释放容器元素指向的对象,需要手动调用循环删除。
for(i= 0; i < vItem.size();i++)
delete vItem[i];
这个链接内,有详细代码描述:vector的clear()的内部过程与析构函数调用_码农小张的博客-CSDN博客
2 常量表达式
值不会改变,并且在编译过程中就能得到计算结果的表达式,即能在编译时求值的表达式。字面值属于常量表达式,常量表达式初始化的 const 对象也是。
其中字面值包括但不限于:算术类型、引用、指针等,在c++中除了void之外的内置类型都是字面值类型。
const int a = 24; // a是常量表达式 && 编译期常量
const int b = a + 1; // b也是常量表达式 && 编译期常量表达式
int c = 8; // c是一个变量
const int d = c; // d属于运行期常量,也是属于常量的。
// 但d不是常量表达式,因为c在程序执行到达其所在的声明处时才初始化
// 所以变量d的值程序运行时才知道,这种写法能够正常编译。
const int len = get_size(); // len不是常量表达式,因为要具体值运行时才能获知
int s[a]; // 正确编译
这也说明了,由关键字const声明的不一定就是常量表达式!
3. constexpr
在实际项目中,是否是常量表达式的判断很难,因此在c++11中引入的新概念 constexpr ,新标准规定,允许将变量声明为 constexpr 类型以便由编译器来验证变量的值是否是常量表达式。
constexpr 只能定义编译期常量表达式,否则编译报错;同时初始化也必须使用常量表达式。
由 constexpr 指定符声明的变量在编译时计算得到函数或者变量的值,该变量一定是一个常量,并且必须使用常量表达式进行初始化。
constexpr 同样可以修饰函数:函数需要满足5个条件:
1. 函数在使用前,必须定义;
2. 函数必须有返回值;
3. 只能存在一条 return 返回语句
4. 返回的必须是常量表达式
5. 参数和返回值必须是字面值类型
使用constexpr修饰的函数,在执行初始化任务时,编译器对 constexpr 函数调用替换成结果值,为了能在编译中随时展开,constexpr函数被隐式地指定为内联函数(inline)。
int func_A(int x) {
return 2 * x;
}
// 需要注意的是函数B返回值不是编译期常量,传入的参数是字面值类型的,但在编译期尚未知晓
// 此时constexpr修饰的函数和普通函数一样
constexpr int func_B(int x) {
return 5 * x;
}
constexpr int func_B() {
return 10;
}
int main() {
constexpr int x = 123; // 编译通过,变量初始化
constexpr int y1 = func_A(123); // 编译报错
constexpr int y2 = func_B(123); // 编译通过,函数初始化,右侧常量表达式
}
错误使用示例:
int main() {
int n;
cin >> n;
const int a = n + 10; // 编译通过,运行期获得结果
constexpr int b = n + 10; // 编译错误
return 0;
}
4. const
const 修饰变量的语义要求编译器阻止所有对该变量的赋值行为,所以必须在 const 变量初始化时就提供初始值。
但是,这个初值可以是编译期确定的值,也可以是运行期确定的值,换句话说const 变量的初始化可以延迟到运行时期。
int main() {
int n;
cin >> n;
const int a = n + 10; // 编译通过,运行期获得结果
int x[n]; // 编译通过
int y[a]; // 编译通过 在作为数组长度时候,是直接替换的
return 0;
}
5. const和constexpr区别
constexpr 的主要功能是使运算可以在编译期完成,并且保证表达式在语义上是类型安全的。因此规定,constexpr 定义的对象必须是编译期常量表达式,其初始化的值也必须是常量表达式。
const 修饰一个对象表示它是常量,这仅仅表达了该对象在初始化后就不再改变!,并未区分出编译期常量 或者 运行期常量,所以为其初始化的值不一定是常量表达式。进一步讲,const 更像是一种 “写权限控制”,即对于那些被 const 修饰的变量,不能通过该变量对所指对象作任何修改(包括常量引用、指向常量的指针、一般常量)。