DEV Community

Ariston
Ariston

Posted on • Edited on

C++基础知识

1.源码转换成二进制的四个步骤

1.预处理:

处理#

2.编译:

进行语法语义分析、把代码转换成汇编代码

3.汇编:

把汇编代码编程二进制文件

4.链接:

把可执行文件进行打包

2.动态库与静态库的区别

静态库会链接到可执行程序、动态库不会

3.常量指针和指针常量就

是看p到底指向的是谁,是const int(int const)还是指向的是int类型

4.关于new和delete以及三种智能指针的联动

1.unique_ptr


Enter fullscreen mode Exit fullscreen mode

2.shared_ptr

多个指针共享一块对象的使用资源,参数是对象,也可以传入右值
具有bool值

shared_ptr<Person> ptr= make_shared<Person>(new Person("name",12));
//创建一个shared_ptr
ptr.reset();//释放ptr所指的对象
(*ptr).someMethod();
ptr->someMethod();
ptr.user_count();//为0时所指资源被释放
Enter fullscreen mode Exit fullscreen mode

可以进行赋值,就是把对资源的控制权转移出去,就是=
删除器:是当shared_ptr被销毁时不是执行delete,而是执行删除器中的函数。创建shared_ptr的第二个参数,是一个函数指针

5.类和对象部分

深拷贝和浅拷贝:浅拷贝是值复制过去了、深拷贝则是重新new一个对象赋值过去。
拷贝构造函数的形参为const是为了可以绑定右值,而&则是为了不让形参实参相结合不断调用拷贝构造函数。
关于静态成员函数不能访问非静态成员的问题:静态成员函数属于整个类,没有this指针,而非静态数据成员属于对象,所以前者无法访问后者
友元要在被破坏封装性的函数中进行声明。
为什么推荐用友元来进行运算符重载?
对称性,如果是成员函数的话,左边必须是类类型,但是是友元函数则比较灵活

拷贝构造函数甚至能说出四种,构造函数,拷贝构造函数,赋值运算符函数、移动构造函数(只是转移资源的所有权)

const 对象成员 引用必须在初始化列表中完成初始化。

在继承中,基类的私有成员总是不可访问的,而公有和保护成员的访问权限取决于继承方式,公有继承保持原有权限,保护继承将所有成员降级为保护,私有继承将所有成员降级为私有。
派生类构造函数不是先执行基类构造函数然后执行自己的派生类构造函数,而是为了完成对基类吸收过来的数据成员的初始化。派生类对象被销毁时,先调用派生类,中间还有普通成员对象,然后调用基类

多态的三种方式:虚函数、重载、模板
虚函数内存布局

Image description

用基类指针来玩派生类的时候,基类必须要全部设置为虚析构。

派生适用于基类的全部场景。
对于派生类和基类的拷贝构造函数,如果派生类没有定义拷贝构造函数或者赋值运算符函数,那么在派生类进行复制的时候会只走基类的拷贝构造函数或赋值运算符函数,这是一种缺省的行为;当派生类和基类都定义了,如果想要对基类中的进行复制,则需要显示调用拷贝构造函数

重定义和隐藏:重定义是派生类函数overwrite基类函数,隐藏则是非虚函数,派生类声明了一个和基类函数名字相同的

多继承

对于多继承可以采用类名+作用域限定符的形式进行调用基类中的同名函数。
菱形继承问题:会造成基类的实例在派生类中重复
虚继承:为了解决菱形继承问题,即使有多个中间类虚拟继承虚基类,那么也只会调用一次虚基类的构造函数
虚基类:被虚拟继承的类,其本身并不声明什么

6,内存对齐

概念说的很蠢,但是可以直接上手做,试试看

7.STL部分

容器

序列式容器:

1.vector

扩容原理:capacity是2倍扩容,且不是在原有位置上,

关于push_back和emplace_back的区别:
push_back使用复制或移动(拷贝构造函数和移动构造函数),这种情况会造成临时资源的创建和析构造成资源浪费;emplace_back使用的是perfect-forwarding,直接在vector中就地构造对象,一般来说更快。

2.list

3.deque

关联式容器

1.set

元素唯一、不能重复、底层红黑树。

2.map

key-value、key不重复、底层红黑树

无序关联式容器

10.移动语义部分:

11.线程部分

线程安全:多个线程访问同一份资源时不会引起资源损坏或数据不一致

std中的thread

std::thread t(thread_function);
std::thread::id this_id = std::this_thread::get_id();
t.detach();
Enter fullscreen mode Exit fullscreen mode

其中detach慎用,分离后可能主线程结束,t还在进行,可能会造成某些未知错误。

mutex和condition_variable

拿到mutex访问共享资源,访问结束释放共享资源

void function(){
mtx.lock();
//操作共享数据
mtx.unlock();
}
Enter fullscreen mode Exit fullscreen mode

但我们有更方便的方式

void function(){
unique_lock<std::mutex> lock(mtx)
//操作共享数据
}
Enter fullscreen mode Exit fullscreen mode

关于条件变量condition_variable
顺带用一下生产者消费者模式

//首先记住,先声明四个变量
mutex mtx
condition_variable cv;
queue<int> data_queue;
bool done = false;

void producer(){
//先往缓冲区队列push进去10个
for(int i = 0;i < 10; ++i){
unique_lock<mutex> lock(mtx);
data_queue.push(i);
lock.unlock();//这里是为了在能够及时通知其他线程资源池中有资源了,更加细粒度
cv.notify_one();
}
done = true;
cv.notify_all();
}

void consumer(){
/*消费者和生产者有两个不同的逻辑,如下所说
当前生产者消费者模型中,生产者生产完了这个函数就执行完毕了
而消费者如果没消费完就要一直等着,而当生产者全部把资源放进queue了后,done变为true,并且队列没有了,说明也就没有资源了也不会再有资源了,所以此时退出。
*/
while(true){
unique_lock<mutex> lock(mtx);
cv.wait(lock,[]{return !cv.empty() || done;});
if(done && cv.empty()){
break;
}
int data = data_queue.front();
data_queue.pop();
lock.unlock();
}
}
Enter fullscreen mode Exit fullscreen mode

pthread

int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
//分别是线程id指针,属性一般为空、线程入口函数、线程的参数
//注意全部是指针

pthread_exit(thread_result);
int pthread_join(thread_id,&thread_result);
//当前线程等待tid的结束,并得到一个tid的返回值
pthread_t threadID = pthread_self();

Enter fullscreen mode Exit fullscreen mode

Top comments (0)