针对《C++八股文-小贺》这个pdf的内容记录的笔记
release()
用于将智能指针内部的指针置为nullptrget()
用于获得其中的原生指针auto_ptr
:只能有一个auto_ptr
指向一个原生指针,新的auto_ptr
指向原生指针时,调用原来的auto_ptr
会报错(但不会出现编译错误)unique_ptr
:和auto_ptr同理,一个原生指针只能被一个unique_ptr
指向,若多个unique_ptr
指向同一原生指针会出现编译错误shared_ptr
:可以有多个shared_ptr
指向同一原生指针,每个shared_ptr
相当于原生指针的一个引用,当原生指针的所有引用都被销毁时原生指针释放(通过一个计数器确定引用数),可通过use_count()
查看资源引用个数weak_ptr
:该指针是配合shared_ptr
使用的,它只能由shared_ptr
或其他weak_ptr
构造。该指针不会对计数进行影响。weak_ptr
可用来解决shared_ptr
的循环引用问题 auto p = make_shared<int>(1);
auto q = p;//此时1的引用数为2
auto r = make_shared<int>(2);//此时2的引用值为1
r = p;//此时1的引用值变为3,2的资源被回收,引用值为0
class A {
public:
shared_ptr<int> b;
};
class B {
public:
shared_ptr<int> a;
}
int main() {
auto a_ = make_shared<A>();
auto b_ = make_shared<B>();
a_.b = b_;
b_.a = a_;//此时出现循环引用
cout << a_.use_count() << " " << b_.use_count();
//输出结果为2 2,在函数结束时两个共享指针的计数都减一,但仍不为0,所以不会释
//放内存
}
const
修饰指针 const int* p = 10;//此处限定值不能改变,即p中的值10不能改变,但p可以指向其他地址
int* const q = 10;//此处限定指针指向不能改变,即q不能指向其他地址,但值10可以改变
const int* const r = 10;//以上两者的结合
//可以根据const与*的位置判断
const
修饰成员变量
const
成员变量值可以不同,不能在类申明时确定值,一般在构造函数的初始化列表中初始化。 #include <iostream>
using namespace std;
class A{
private:
const int p;
public:
A(int a): p(a){//在此初始化
}
void print() {
cout << p << endl;
}
};
int main() {
A a(2);
A b(3);
a.print();
b.print();
//输出结果为2 \n 3
}
const
修饰类成员函数
void func(int i) const {};
int global;
void func(int i) const {global = i;}//错误,不能修改外部数据
static_cast
double a = 1.1;
int b = static_cast<int>(a);
dynamic_cast
class A{};
class B: public A{};
class C: public C{};
int main() {
A* pa = new A();
B* pb = dynamic_cast<A*>(pa);//转型失败,因为pa指向的对象(A)无法安全转型
pa = new C();
B* pb = dynamic_cast<A*>(pa);//转型成功,pa指向的对象(C)能够安全转型
}
malloc
与free
仅用于分配内存空间,而new
与delete
在声明/回收内存空间后/前还需要执行构造函数/析构函数class Base {
public:
virtual void funcA() {cout << "Base A";}
void funcB() {cout << "Base B";}
};
class Derived : public Base {
public:
void funcA() final {cout << "Derived A";}
void funcB() {cout << "Derived B";}
};
int main() {
Base* p = new Derived();
p.funcA();
p.funcB();
//输出结果:
//Derived A
//Base B
//定义为虚函数的函数会根据对象进行动态绑定
//非虚函数则会根据指针类型进行早绑定,与对象所属类无关
}
因为值传递方式会调用对象的拷贝构造函数,而调用拷贝构造函数又是值传递方式,会造成无限递归
#include
的头文件内容插入程序中,将#define
的内容进行替换等等,基本上是将所有以#开头的部分进行操作deque
的内存是由多段连续内存组成的,它设置了一个map用于储存连续地址的首地址,真正的数据缓存地址是在这些连续地址中。迭代器由first,last,cur,node四个部分组成,其中first指向当前缓冲区头部,last指向当前缓冲区尾部,cur指向缓冲区现行元素,node为map中指向当前缓冲区的node节点。vector
,使用push_back
时,若内存不足,则会重新分配一块更大的空闲内存,并将当前内容拷贝过去,比较消耗资源。push_back
vs emplace_back
push_back
只能传入符合模板的对象,而emplace_back
可直接传入构造函数的参数来插入新的对象。push_back
插入对象的方式为:在临时内存空间创建对象,再将其拷贝至容器的内存中emplace_back
插入方式为:直接在容器对于的内存位置创建新对象,相对来说少了拷贝的一步emplace_back
在使用构造参数方式传入新对象时,效率会高于push_back
erase()
函数删除迭代器vector
,deque
等内存地址连续的容器,删除方法如下 vector<int> vec = {1, 2, 3, 4, 5, 6};
vector<int>::iterator iter = vec.begin();
while (iter != vec.end()) {
if ((*it) % 2 == 0) {
it = vec.erase(it);
}
else {
it ++;
}
}
erase()
函数会返回下一个有效的迭代器map
,set
这种关联容器,直接++即可 map<string, int> mp;
map<string, int>::iterator iter = mp.begin();
while (iter != mp.end()) {
if ((*it) % 2 == 0) {
it = vec.erase(it);
}
it ++;
}
list
这种不连续分配内存的容器,以上两种方法都可以___attribute(constructor) void before() {}
static int a = before();//全局静态变量在main函数之前初始化
int a = []() {cout << "before main";}\\lambda表达式
int a = 0x12345678;
int *b = &a;
cout << b[0];//若为0x12则为大端,若为0x78则为小端
int main() {
union{
int a;
char b;
} data;
data.a = 1;
cout << data.b;//若输出1,则代表a的低位与b的部分共用,及低字节在低地址,为小端
}
[ capture-list ] ( params ) mutable(optional) constexpr(optional) \
(c++17) exception attribute -> ret { body }
[]
代表捕捉列表,即从当前作用域中捕获变量,捕获的变量可在函数体内直接使用
[=]
: 表示以值方式捕获所有变量[&]
: 表示以引用方式捕获所有变量[x]
: 值捕获x[&x]
: 引用捕获x[=, &x]
: 所有变量值捕获,除x使用引用捕获[&, x]
: 所有变量引用捕获,除x值捕获()
代表参数列表mutable
使得值捕获的变量可以被修改constexpr
可指定该lambda函数为常量函数exception
可指定该lambda函数抛出的异常attribute
可指定lambda表达式的特性ret
指定返回值类型{}
函数体move
函数,直接将资源进行转移,而不是创建临时对象后进行拷贝 A get() {
A p;
return a;
}
Person p = get();
class A {
int val;
A(int x) {val = x;}
A(A&& rhs) {//移动构造函数
val = rhs.val;
}
};
int main() {
A a = new A(std::move(A(10)));
}
class A {
virtual void func(){}
};
class B : public A {
void func() override final {}
}
class C : public B {
//void func() {} 禁止,因为B的func函数已经声明final
}
静态static_assert一般用于模板,因为模板是编译期的概念,无法使用assert
用例如下
template< class T >
struct Check {
static_assert( sizeof(int) <= sizeof(T), "T is not big enough!" ) ;
} ;//T在编译期间确定,所以使用static_assert
new
创建一个对象,首先调用operator new
分配内存空间,再调用对象构造函数初始化这段内存new operator
,operator new
,placement new
new operator
不可更改,作用同上operator new
可在类中重载placement new
语法:T *p = new (ptr) T()
,其中ptr为一个缓冲区指针。placement new
的意义是将缓冲区的分配与对象的构造分离。你可以先构造一个缓冲区如下char *ptr = new char[sizeof(T)]
,然后再使用placement new
,这样就让缓冲区的创建和对象的创建分离。 template<typename T1, typename T2>
inline void construct(T1* p, const T2& value) {
new (p) T1(value);
}
template<typename T1>
inline void construct(T1* p) {
new (p) T1;
}
template<typename T1>
inline void destroy(T1* p)
{
p->~T1();
}
destroy
还有其他模式,暂且不谈sgi_alloc.h
中alloc
为STL动态分配空间alloc
分为两级配置器,其目的是解决内存碎片问题malloc()
,free()
等函数分配内存,并在内存不足时调用oom_malloc
,oom_realloc
等函数尝试释放内存free_list
的结构,它是一个长度为16的指针数组,用于维护一个链表,这段内存空间由一组union
组成 union Obj{
union Obj *p;
char data[1];//两个等长指针,可用于存储数据或作为list指针
};
Obj
作为指针指向下一个内存块;存储数据时,作为缓冲区储存数据free_list
的i位分别表示其指向的内存块大小为(i + 1) * 8B define __ALIGN = 8;
static size_t ROUND_UP(size_t bytes) {
return (bytes + __ALIGN - 1) & ~(__ALIGN - 1);
}
free_list
中寻找合适的内存块并取出free_list
)free_list
中拿出一块作为内存池func(I iter)
,需要接受各种类型的迭代器。若此时func
函数的实现中需要知道*iter
对于的数据类型(比如需要一个临时变量,但不知道类型不方便声明),则有以下方法
func
中内嵌如下函数,则可以通过C++的类型推导得到对应的类型 template<class I>
inline void func(I iter) {
func_imp(iter, *iter);
}
template<class I, class T>
void func_imp(I iter, T t) {
//执行func需要执行的操作
}
template<typename T>
class MyIter {
public:
typedef T value_type; //内嵌类型声明
MyIter(T *p = 0) : m_ptr(p) {}
T& operator*() const { return *m_ptr;}
private:
T *m_ptr;
};
template<class MyIter>
typename MyIter::value_type func(MyIter iter) {
return *iter;
}
template<typename I>
class A{}//泛化版本
template<typename I>
class A<I*>{}//特化版本,只针对原生指针
iterator_traits
类(traits,萃取) template<typename Iterator>
struct Iterator_traits {
//类型萃取机
typedef typename Iterator::value_type value_type; //value_type 就是 Iterator 的类型型别
}
template<typename Iterator>
struct Iterator_traits<Iterator*> {
typedef Iterator value_type;
}
func
改为以下形式 template<typename Iterator>
typename Iterator_traits<Iterator>::value_type func(Iterator iter) {
return *iter;
}
Traits编程技法
。value_type
:迭代器所指对象的类型difference_type
:表示迭代器之间的距离reference_type
:表示迭代器所指对象的引用pointer_type
:表示迭代器所指对象的指针iterator_category
:表示迭代器的类型,迭代器有五种类型
Input Iterator
:只读迭代器,支持* -> == != ++
等操作Output Iterator
:只写迭代器,支持++ *
等操作Forward Iterator
:单向移动的读写迭代器,支持只读只写的所有操作Bidirectional Iterator
:双向移动的读写迭代器,支持单向读写器所有操作加上-
Random Iterator
:随机访问的读写迭代器,支持前四种迭代器所以操作加上[]