C/C++基础知识点(三)

文章目录

        • 25.STL中迭代器的作用,有指针为何还要迭代器?
        • 26.C++中struct和class的区别
        • 27.什么是右值引用?跟左值有什么区别?
        • 28. STL中resize和reserve的区别
        • 29.C++内存管理
        • 30.什么时候会发生段错误
        • 31.什么是内存泄漏
        • 32.C++11常用的新特性
        • 33.C++11中的可变参数模板
        • 34.单核机器上写多线程程序,是否需要考虑加锁,为什么
        • 35.线程和进程的区别
        • 36.多线程和多进程的使用场景

25.STL中迭代器的作用,有指针为何还要迭代器?

1.迭代器
iterator(迭代器)模式又称Cursor(游标)模式,用于提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示。
由于迭代器的以上特性:与聚合对象耦合,在一定程度上限制了它的广泛运用,一般仅用于底层聚合支持类,如stl中list、vector、stack等容器类以及ostream_iterator等扩展iterator。

2.迭代器与指针的区别
迭代器不是指针,是类模板,表现得像指针。它只是模拟了指针的一些功能,通过重载了指针的一些操作符:-> * ++ – 等。迭代器封装了指针,是一个“可遍历STL容器内全部或部分元素”的对象,本质是封装了原生指针,是指针概念的一种提升,提供了比指针更高级的行为,相当于一种智能指针,它可以根据不同类型的数据结构来实现不同的++ 和–等操作。
迭代器返回的是对象引用而非对象的值,所以只能输出*取值后的值而不能直接输出其自身。

3.迭代器产生的原因
iterator类的访问方式就是把不同集合类的访问逻辑抽象出来,似的不用暴露集合内部的结构而达到循环遍历集合的效果。

26.C++中struct和class的区别

在C++中,可以使用struct和class定义类,都可以继承。区别在于:struct的默认继承权限和默认访问权限都是public,而class的默认继承权限和访问权限都是private。此外,class还可以定义模板类形参,比如:template

27.什么是右值引用?跟左值有什么区别?

右值引用是C++11中引入的新特性,它实现了转移语义和精确传递。它的主要目的有两个方面:
1.消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
2.能够更简洁明确地定义泛型函数。

左值和右值的概念:
左值:能对表达式取地址、或匿名对象/变量。一般指表达式结束后依然存在的持久对象。
右值:不能对表达式取地址,或匿名对象。一般指表达式结束就不在存在的临时对象。

右值引用和左值引用的区别:
1.左值可以寻址,而右值不可以
2.左值可以被赋值,右值不可以被赋值,可以用来给左值赋值
3.左值可变,右值不可变(仅对基础类型适用,用户自定义类型右值引用可以通过成员函数改变)

详细可参考这里

28. STL中resize和reserve的区别

resize既分配了空间,也创建了对象,可以通过下标访问。当resize的大小
reserve只修改capacity大小,不修改size大小,resize既修改capacity大小,也修改size大小。

reserve是容器预留空间,但并不真正创建元素对象,在创建对象之前,不能引用容器内的元素,因此当加入新的元素时,需要用push_back()/insert()函数。
resize是改变容器的大小,并且创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。

resize和reserve接口的共同点是它们都保证了vector的空间大小(capacity)最少达到它的参数所指定的大小。

需要注意的是:

对于resize,假如resize的大小为n,那么:

  • 如果n比当前的vector元素数目要小,vector的容量要缩减到resize的第一个参数大小,既n。并移除那些超出n的元素同时销毁他们。

  • 如果n比当前vector元素数目要大,在vector的末尾扩展需要的元素数目,如果第二个参数val指定了,扩展的新元素初始化为val的副本,否则按类型默认初始化。

  • 注意:如果n大于当前的vector的容量(是容量,并非vector的size),将会引起自动内存分配,会很耗时。

29.C++内存管理

在C++中,虚拟内存分为代码段、数据段、BSS端、堆区、栈区、文件映射区六部分。

  • 代码段:包括只读存储区和文本区,其中只读存储区存储字符串常量,文本区存储程序的机器代码;
  • 数据段:存储程序中已初始化的全局变量和静态变量;
  • bss段:存储未初始化的全局变量和静态变量(局部+全局),以及所有被初始化为0的全局变量和静态变量;
  • 堆区:调用new/malloc函数时再堆区动态分配内存,同时需要调用delete/free来手动释放申请的内存;
  • 映射区:存储动态链接库以及调用mmap函数进行的文件映射;
  • 栈:使用栈空间存储函数的返回地址、参数、局部变量、返回值;

30.什么时候会发生段错误

段错误通常发生在访问非法内存地址的时候,具体来说分为以下几种情况:

  • 使用野指针
  • 试图修改字符串常量的内容

31.什么是内存泄漏

内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。
内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成的内存浪费。

内存泄漏分类:

  • 1.堆内存泄漏:堆内存指的是程序运行中根据需要分配的一段内存,完成后必须通过调用delete/free删掉,如果没有及时释放,那么此后这段内存将不会被使用,就会产生Heap Leak。
  • 2.系统资源泄漏:主要指程序使用系统分配的资源,比如Bitmap、handle、socket等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。
  • 3.没有将基类的析构函数定义为虚函数。当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确释放,因此造成内存泄漏。

32.C++11常用的新特性

  • auto关键字:编译器可以根据初始值自动推导出类型,。但是不能用于函数传参以及数组类型的推导。
  • nullptr关键字:nullptr是一种特殊类型的字面值,它可以被转换成任意其它的指针类型;而NULL一般被宏定义为0,在遇到重载时可能会出现问题。
  • 智能指针:C++11新增了std::shared_ptr、std::weak_ptr等类型的只能指针,用于解决内存管理问题。
  • 初始化列表:使用初始化列表来对类进行初始化。
  • 右值引用:基于右值引用可以实现移动语义和完美转发,消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率;
  • atomic原子操作用于多线程资源互斥操作;
  • 新增STL容器array以及tuple;

33.C++11中的可变参数模板

C++11的可变参数模板,对参数进行了高度泛化,可以表示任意数目、任意类型的参数,其语法为:在class或typename后面带上省略号。
比如:

Template<class ... T>
void func(T ... args)
{
	cout << "num is" << sizeof ...(args) <<endl;
}

func() //args不含任何参数
func(1) // args包含一个int类型的实参
func(1,2.0) //args包含一个int和一个double类型的实参
其中T叫做模板参数包,args叫做函数参数包

省略号的作用如下:

  • 声明一个包含0到任意个模板参数的参数包
  • 在模板定义的右边,可以将参数包展开成一个个独立的参数

C++可以使用递归的函数的方式展开参数包,获得可变参数的每个值。通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数。

比如:

#include 

using namespace std;

void print()
{
    cout << "empty" << endl;
}

template <class T,class ... Args>
void print(T head,Args ... args)
{
    cout << head << "," ;
    print(args...);
}

int main()
{
    print(1,2,3,4);
    return 0;
}

输出:1,2,3,4,empty
参数包Args…在展开的过程中递归调用自己,每调用一次参数包中的参数就会少一个,直到所有参数都展开为止,当面没有参数时就会调用非模板函数print终止递归过程。

34.单核机器上写多线程程序,是否需要考虑加锁,为什么

在单核机器上写多线程程序,仍然需要线程锁。
因为线程锁通常用来实现线程的同步和通信。在单核机器上的多线程程序,仍然存在线程同步的问题。因为在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,不使用线程锁的前提下,可能会导致共享数据修改引起冲突。

35.线程和进程的区别

  • 进程是CPU资源分配的最小单位,线程是cpu调度的最小单位
  • 进程有独立的系统资源,而同一进程内的线程共享进程的大部分系统资源,包括堆、代码段、数据段,每个线程只拥有一些在运行中必不可少的私有属性,比如线程id,栈,寄存器等
  • 一个进程崩溃,不会对其他进程产生影响,而一个线程崩溃,会让同一进程内的所有其他线程也死掉
  • 进程在创建、切换和销毁时的开销比较大,而线程则比较小。进程创建的时候需要分配系统资源,而销毁的时候需要释放资源,进程切换需要分两步:切换页目录,刷新TLB以使用新的地址空间;切换内核栈和硬件上下文(寄存器);而同一进程的线程间逻辑地址控件是一样的,不需要切换页目录,刷新TLB;
  • 进程间通信比较复杂,而同一进程的线程由于共享代码段和数据段,所以通信比较容易。

36.多线程和多进程的使用场景

多进程模型的优势是CPU,多线程模型主要优势为线程间切换代价较小。
因此多线程适用于I/O密集型的工作场景,因为I/O密集型的工作场景经常会由于I/O阻塞导致频繁的切换线程。同时, 多线程模型也适用于单机多核分布式场景。

多进程模型,适用于CPU密集型。同时,多进程模型也适用于多机分布式场景中,易于多机扩展。

你可能感兴趣的:(C++,And,C)