C++笔记摘要

一、基础知识:
1、C++与C的区别
1)设计思想:

  C++是面向对象,而C是面向过程的结构化编程语言。

2)语法上:

  C++具有封装性、继承、多态三种特性;
  C++相比C,增加许多类型安全的功能,比如强制类型转换;
  C++支持范式编程,如模板类、函数模板等

2、C++中static关键字的作用
1)局部静态变量:

   一般情况下,局部变量是放在栈区的,并且局部变量的生命周期是在该代码快执行完后就结束;若加了static修饰,其作用域和存储位置发生变化,要到程序结束后才会结束生命,除了代码块其他地方虽然变量存在但是使用不了。

2)全局静态变量:

  在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。
  存储区: 静态存储区,整个程序运行期间一直存在;
  初始化: 未被初始化的静态全局变量会被自动初始化为0;
  作用域: 在声明它的文件之外是透明的(不可见,除非用extren关键字),准确是从定义之处开始,到文件结尾。

3)修饰函数:

  和修饰全局变量基本一致,就是改变了函数的作用域;

4)C++的static

  若类中某个函数为static,表明该函数属于类并非属于任何一个类的对象;若成员变量为static,则说明该变量属于类和类对象,只保存一次副本,可以通过类和对象进行访问。

3、C++中四种类型转换
1)const_cast:

  用于将const变量转换为非const;

2)static_cast:

  用于各种隐式转换,如非const转const,void*转指针等;static_cast多用于多态向上转化,如果向下转能成功但是不安全,结果也未知。

3)dynamic_cast:

  用于动态类型转换。只能用于含有虚函数的类,用于类层次之间的向上和向下转换;只能转指针或引用。向下转换时,如果是非法的对于指针返回NULL,对于引用抛异常。
   - 向上转换:子类向基类的转换;
   - 向下转换:基类向子类的转换;

4)reinterpret_cast:

  几乎什么都可以转,比如int转int*,可能会出问题,尽量少用。

思考:为什么不使用C的强制类型转换

  C的强制类型转换表面上看起来功能强大什么都能转,但是转化不够明确,不能进行错误检查,容易出错。

4、指针和引用的区别

  1) 指针有自己的一块空间(存储的内容为对象地址),其sizeof大小为一个int4;而引用只是一个别名,其大小是被引用对象的大小;
  2) 指针可以初始化为NULL;而引用必须被初始化且必须是一个已有对象的引用;
  3) 指针需要被解引用才可以对对象进行操作,而引用直接修改即可,两则都会修改原有对象
  4) 指针可以多级指针(**p),而引用只有一级;
  5) 指针和引用运算符++的意义不一样;
  6) 若要返回动态内存分配的对象或者内存地址,必须使用指针,引用可能会引起内存泄漏。

补充:const的含义与机制:

const名叫常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动:
1)修饰一般常量和数组: const放在类型前或者后是等效的,说明该变量或数值内容是常量,不允许修改;
2)修改指针变量*及引用&: 若const位于*的左侧,说明是常量,指针指向的内容或者引用内容不可修改;若在右侧,const修饰的是指针或者引用,指针指向或者引用不可更改,内容可以改。
3)const修改函数的参数: 说明在函数体中不能修改其内容
4)const修改函数返回值: 说明保护返回值不被更改
5)const在类中的用法: 不能在类声明中初始化const数据成员。正确的使用const实现方法为:const数据成员的初始化只能在类构造函数的初始化表中进行类中的成员函数:A fun4()const; 其意义上是不能修改所在类的的任何变量。
6)修饰类对象: 表示常量对象,常量对象只能调用常量函数,别的成员函数都不能调用。

5、include投文件的顺序以及双引号""和尖括号<>的区别

   ** 1)** include顺序: 若在文件a.h中声明一个在文件中b.h中定义的变量,而不引用b.h,那么要在a.c文件中引用b.h文件,并且要先引用b.h,后引用a.h,否则报变量类型未声明错误。
   ** 2)** 双引号和尖括号的区别: 编译器预处理阶段查找头文件的路径不一样:
    ----双引号:当前头文件目录–>编译器设置的头文件路径(编译的时候可以通过-l显示指定搜索路径)–>系统环境变量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的头文件路径;
    ----尖括号:编译器设置的头文件路径(-l指定)–>系统环境变量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的头文件路径。

6、数组和指针的区别

  **1)指针:**保存数据的存储单元地址,间接访问数据,先获取指针的内容,然后再寻址提取里面的内容。通常用于动态的数据结构,通过Malloc分配内存,free释放内存。
  **2)数组:**直接访问数据,通过用于固定数目且数据类型相同的元素隐式的分配和删除。

7、野指针

  野指针就是指向一个已删除的对象或者申请访问受限内存区域的指针。

8、四个智能指针
为什么使用引入智能指针:

  智能指针的作用是管理指针。引入原因:当申请的空间在函数结束时忘记释放,会造成内存泄漏。使用智能指针可以很大程度上避免上面这种情况,因为智能指针本质是一个类,当超出了类的作用域。类会自动调用析构函数,析构函数会自动释放资源。因此引入智能指针的作用就是在函数结束是自动释放内存空间,不需要手动释放

1)auto_ptr(C++98的方案,C++11已经弃用):

   采用所有权模式:

auto_ptr<string> p1 (new string("I love China!"));
auto_ptr<string> p2;
p2=p1;// auto_pt不会报Error
std::cout<<*p1<<std::endl; // Error

此时运行代码不会保错,但是p2剥夺了p1的所有权,而p1成了野指针。若程序运行时访问p1将会报错。因此auto_ptr的缺点是:存在潜在的内存崩溃问题

2)unique_ptr(替换auto_ptr):

   unique_ptr实现独占式拥有和严格拥有概率,保证同一时间内只用一个智能指针可以指向该对象。它可以避免资源泄漏(例如以new创建对象后因为发生异常而忘记delete)
&emps;&emps;采用所有权模式

unique_ptr<string> p3 (new string("auto!"));
unique_ptr<string> p4 = p3;// not allowed , Error

编译器认为p4=p3非法,避免了p3不再指向无效数据的问题,因此比auto_ptr更安全;
另外,unique_ptr还有更值得亮点的地方:当程序试图将一个unique_ptr赋值给另一个时,如果unique_ptr是个临时右值(#2),编译器允许这么做;如果源unique_ptr将存一段时间,就禁止这种操作如(#1)

unique_ptr<string> pu1 (new string("hello word!"));
unique_ptr<string> pu2;
pu2=pu1; // #1 not allowd
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string("Me!")) // #2 allowd

其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销毁。这种行为表明,unique_ptr 优于允许两种赋值的auto_ptr 。
Warning: 若想实现#1赋值,也可以实现,具体见如下代码。C++具有一个标准库函数std::move(),能够将一个unique_ptr赋值给另外一个。

unique_ptr<string> ps1,ps2;
ps1 = unique_ptr<string>(new string("C++"));
ps2 = std::move(ps1); // allowed
ps1 = unique_ptr<string>(new string("Plus"));
std::cout<<*ps1<<" "<<*ps2<<std::endl;
3)shared_ptr(解决auto_ptr独占):

   shared_ptr实现共享式 拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在最后一个引用被销毁 时候释放。从名字share就可以了解到资源可以被多个指针共享,它使用技术机制来表示资源被几个指针共享,可以通过成员函数use_count()来查当前资源的所有者个数。除了可以通过new来构造外,还可以通过传入auto_ptr、unique_ptr、weak_ptr来构造。当调用release()时,当前指针会释放资源所有权,计数减1,当计数为0时,资源会被释放。
相关成员函数说明:
   ---- use_count():返回引用计数的个数
   ---- unique():返回是否是独占所有权(use_count()是为1)
   ---- swap():交换两个share_ptr对象
   ---- reset():放弃内部对象的所有权或拥有对象的变更,会引起原有对象的引用计数的减少
   ---- get():返回内部对象(指针),由于已经重载了shapred_ptr()方法,因此和直接使用对象是一样,如下代码:

void testSmartPoint() {
   
	std::shared_ptr< int> sh_pt(new int(1));
	std::cout <<"sh_pt:"<< sh_pt << std::endl;
	std::cout << "sh_pt.get():"<<sh_pt.get() << std::endl;
}

在这里插入图片描述

4)weak_ptr:

   weak_ptr是一种不控制对象生命周期 的智能指针,它指向一个shared_ptr管理的对象,进行该对象的内存管理的是那个强引用的。shared_ptr.weak_ptr()只是提供了对管理对象的一个访问手段。weak_ptr设计的目的是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它只可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造函数和析构函数不会引起引用计数的增加或减少。 weak_ptr是用来解决shared_ptr相互引用时产生死锁问题。如果说两个shared_ptr相互引用,那么两个指针的引用计数永远不可能下降为0,资源永不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以进相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

class B;
class A {
   
	public:
		std::shared_ptr<B> _pb;
		~A() {
   
			std::cout << "A delete" << std::endl;
		}
};
class B {
   
	public:
		std::shared_ptr<A> _pa;
		~B() {
   
			std::cout << "B delete" << std::endl;
		}
};
void testSmartPoint() {
   

	std::shared_ptr<B> pb(new B());
	std::shared_ptr<A> pa(new A());
	// 相互引用
	pb->_pa = pa;
	pa->_pb = pb;
	std::cout << pb.use_count() << std::endl; 
	std::cout << pa.use_count() << std::endl; 
}
int main(int argc, char* argv[]) {
   
	testSmartPoint();
}

在这里插入图片描述
   在testSmartPoint函数中,pa,pb之间相互引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减1,但是两者引用计数还是为1,导致跳出函数时资源未被释放(A和B的析构函数均没有被调用)。如果把其中的一个改为weak_ptr就可以了,把类A里面的shared_ptr pb;改为weak_ptr pb;运行结果如下所示。这样的话,资源B的引用开始就只有1,当析构pb时,B的计数变为0,B得到释放,那么会使A的计数减一,同时pa析构时使A的计数减1,那么A的计数为0,A就得到释放。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值