赋值
构造函数实现的是值拷贝传递。当类的数据成员为指向堆上的
指针
时,值拷贝会使多个类的
指针
指向同一块内存。当其中一个类释放这块内存时,会照成其他类的这个
指针
空悬,从而引来风险。
智能
指针
是用来解决这类问题的方法。它终的目的是让多份
指针
安全地指向同一块内存。要做到这一点,这块被指向的内存只能在后一个
指针
所在的类被析构时,才被释放。
网上的资料显示智能
指针
有引用辅助类和使用句柄类这两种实现方法。但从本质上看,都是实现:
1、使得智能
指针
这个类拥有与
指针
相似的使用方法。(通过重载*,-
&
gt;等符号可以实现)
2、需要借助一块堆上的内存来
指针
(pointer)是“指向(point to)”另外一种类型的复合类型,它实现了对其它对象的间接访问。定义
指针
类型的方法将声明符写成*d的形式,其中d是变量名,如以下代码声明了一个整形
指针
:
int *ip1;
首先,什么叫做
指针
的初始化?
int * p = NULL;在定义
指针
变量p的同时把p的值设置为0x00000000;而不是把*p的值设置为0x00000000。这个过程叫做初始化。
探讨: int * p =
&
amp; a; 和 int * p =
&
amp;(int )0x0012ff60; 的含义和区别
(1)我们来看第一段代码:
#include
&
lt;stdio.h
&
gt;
指针
变量的
赋值
指针
变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。未经
赋值
的
指针
变量不能使用,否则将造成系统混乱,甚至死机。
指针
变量的
赋值
只能赋予地址,决不能赋予任何其它数据,否则将引起错误。在C语言中,变量的地址是由编译系统分配的,对用户完全透明,用户不知道变量的具体地址。
C语言中提供了地址运算符
&
amp;来表示变量的地址。其一般形式为:
&
amp; 变量名; 如
&
amp;...
当我们声明了一个
指针
之后,这个
指针
变量中没有存储任何确定的地址值,而是一个随机整数。也就是它指向的地址是不确定的,有可能它指向了系统的重要数据,这时候我们如果修改了它指向地址的值可能会引起想象不到的系统问题。所以
指针
声明以后要先
赋值
才可以引用。
给
指针
变量
赋值
也有两种方式:
1.在声明
指针
时对其进行初始化,也就是赋一个初值,初始化形式为:数据类型 *
指针
名=初始地址值;。
原题链接:https://leetcode-cn.com/problems/container-with-most-water/
1、暴力(超时)
int maxArea(vector
&
lt;int
&
gt;
&
amp; height) {
int n=height.size();
int ans=INT_MIN;
for(int i=0;i
&
lt;n;i++){
for(int j=i+1;j
&
lt;n;j++){
ans=max(ans,min
p=
&
amp;c;
这两种写法是相等的。
另外注意野
指针
的概念:(1)野
指针
的错误来源就是
指针
定义了以后没有初始化,也没有
赋值
(总之就是
指针
没有明确的指向一个可用的内存空间),然后去解引用。
(2)知道了野
指针
产生的原因,避免方法就出来了:在
指针
的解引用之前,一定确保
指针
指向一个绝对可用的空间。
(3)常规的做法是:
第一点:定义
指针
时,同时初始化为NULL
第二点:在
指针
解引用之前,先去判断这个
指针
是不是NULL
第三点:
指针
都说C++难学,其中继承这一块的语法就是其中一个体现,像别的语言java就简化了继承这一块的语法(删去了多继承这个语法,就不存在菱形继承的问题),C++中尤其菱形继承(由多继承衍生出来的菱形继承场景)和虚函数、多态结合才是能让人从入门到放弃~
不过这有什么可怕的?只要我们迎难而上,只有难学的知识我们把它搞懂了,才能体现我们和其他人的差距。今天博主来给大家详细剖析C++继承这个语法~
继承的概念
继承的定义
继承关系和访问限定...
C++智能
指针
智能
指针
智能
指针
概念 C/C++ 语⾔最为⼈所诟病的特性之⼀就是存在内存泄露问题,因此后来的⼤多数语⾔都提供了内置内存分配与释放功能,有的甚⾄⼲脆对语 ⾔的使⽤者屏蔽了内存
指针
这⼀概念。这⾥不置贬褒,⼿动分配内存与⼿动释放内存有利也有弊,⾃动分配内存和⾃动释放内存亦如此,这 是两种不同的设计哲学。有⼈认为,内存如此重要的东西怎么能放⼼交给⽤户去管理呢?⽽另外⼀些⼈则认为,内存如此重要的东西怎么能 放⼼交给系统去管理呢?在 C/C++ 语⾔中,内存泄露的问题⼀直困扰着⼴⼤的开发者,因此各类库和⼯具的⼀直在努⼒尝试各种⽅法去检 测和避免内存泄露,如 boost,智能
指针
技术应运⽽⽣。 智能
指针
主要⽤于管理在堆上分配的内存,它将普通的
指针
封装为⼀个栈对象。当栈对象的⽣存周期结束后,会在析构函数中释放掉申请的 内存,从⽽防⽌内存泄漏。简要的说,智能
指针
利⽤了 C++ 的 RAII 机制,在智能
指针
对象作⽤域结束后,会⾃动做内存释放的相关
操作
, 不需要我们再⼿动去
操作
内存。 RAII是C++的发明者Bjarne Stroustrup提出的概念,RAII全称是
&
quot;Resource Acquisition is Initialization
&
quot;,直译过来是
&
quot;资源获取即初始化
&
quot;,也 就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语⾔机制保证了,当⼀个对象创建的时候,⾃动调⽤构造函数,当 对象超出作⽤域的时候会⾃动调⽤析构函数。所以,在RAII的指导下,我们应该使⽤类来管理资源,将资源和对象的⽣命周期绑定。 C++ 中有四种智能
指针
:auto_pt、unique_ptr、shared_ptr、weak_ptr 其中后三个是 C++11 ⽀持,第⼀个已经被 C++11 弃⽤且被 unique_prt 代替,不推荐使⽤。下⽂将对其逐个说明。 std::auto_ptr 在这个年代讨论 std::auto_ptr 不免有点让⼈怀疑是不是有点过时了,确实如此,随着 C++11 标准的出现(最新标准是 C++20),std::auto_ptr 已经被彻底放弃,取⽽代之是 std::unique_ptr。然⽽,之所以还向介绍⼀下 std::auto_ptr 的⽤法以及它的设计不⾜ 之处是想更多了解 C++ 语⾔中智能
指针
的发展过程,⼀项技术如果我们了解它过去的样⼦和发展的轨迹,我们就能更好地掌握它。 std::auto_ptr 的基本⽤法如下代码所⽰: #include
&
lt;iostream
&
gt; #include
&
lt;memory
&
gt; using namespace std; int main() { //初始化⽅式1 std::auto_ptr
&
lt;int
&
gt; ap1(new int(8)); //初始化⽅式2 std::auto_ptr
&
lt;int
&
gt; ap2; ap2.reset(new int(8)); cout
&
lt;
&
lt; *ap1
&
lt;
&
lt;
&
quot;,
&
quot;
&
lt;
&
lt; *ap2
&
lt;
&
lt; endl; return 0; } 输出: 8, 8 智能
指针
对象 ap1 和 ap2 均持有⼀个在堆上分配 int 对象,其值均是 8,这两块堆内存均可以在 ap1 和 ap2 释放时得到释放。这是 std::auto_ptr 的基本⽤法。 std::auto_ptr 真正让⼈容易误⽤的地⽅是其不常⽤的复制语义,即当复制⼀个 std::auto_ptr 对象时(拷贝复制或 operator= 复制),原对象 所持有的堆内存对象也会转移给复制出来的对象。⽰例代码如下: #include
&
lt;memory
&
gt; int main() { //测试拷贝构造 std::auto_ptr
&
lt;int
&
gt; ap1(new int(8)); std::auto_ptr
&
lt;int
&
gt; ap2(ap1); if (ap1.get() != NULL) { std::cout
&
lt;
&
lt;
&
quot;ap1 is not empty.
&
quot;
&
lt;
&
lt; std::endl; } else { std::cout
&
lt;
&
lt;
&
quot;ap1 is empty.
&
quot;
&
lt;
&
lt; std::endl; } if (ap2.get() != NULL) { std::cout
&
lt;
&
lt;
&
quot;ap2 is not empty.
&
quot;
&
lt;
&
lt; std::endl; } else { std::cout
&
lt;
&
lt;
&
quot;ap2 is empty.
&
quot;
&
lt;
&
lt; std::endl; } //测试
赋值
构造 std::auto_ptr
&
lt;int
&
gt; ap3(new int(8)); std::auto_ptr
&
lt;int
&
gt; ap4; ap4 = ap3; if (ap3.get() != NULL) { std::cout
&
lt;
&
lt;
&
quot;ap3 is not empty.
&
quot;
&
lt;
&
lt; std::endl; }
C++智能
指针
的原理和实现 智能
指针
的原理和实现 ⼀、智能
指针
起因 ⼀、智能
指针
起因 在C++中,动态内存的管理是由程序员⾃⼰申请和释放的,⽤⼀对运算符完成:new和delete。 new:在动态内存中为对象分配⼀块空间并返回⼀个指向该对象的
指针
; delete:指向⼀个动态独享的
指针
,销毁对象,并释放与之关联的内存。 使⽤堆内存是⾮常频繁的
操作
,容易造成堆内存泄露、⼆次释放等问题,为了更加容易和更加安全的使⽤动态内存,C++11中引⼊了智 能
指针
的概念,⽅便管理堆内存,使得⾃动、异常安全的对象⽣存期管理可⾏。智能
指针
主要思想是RAII思想,
&
quot;使⽤对象管理资源
&
quot;,在类 的构造函数中获取资源,在类的析构函数中释放资源。智能
指针
的⾏为类似常规
指针
,重要的区别是它负责⾃动释放所指向的对象。 RAII是Resource Acquisition Is Initialization的简称,即资源获取就是初始化: 1.定义⼀个类来封装资源的分配与释放; 2.构造函数中完成资源的分配及初始化; 3.析构函数中完成资源的清理,可以保证资源的正确初始化和释放; 4.如果对象是⽤声明的⽅式在栈上创建局部对象,那么RAII机制就会正常⼯作,当离开作⽤域对象会⾃动销毁⽽调⽤析构函数释放资 源。 ⼆、智能
指针
类型 ⼆、智能
指针
类型 智能
指针
在C++11版本之后提供,包含在头⽂件
&
lt;memory
&
gt;中,标准命名std空间下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四 种,其中auto_ptr已被弃⽤。 :拥有严格对象所有权语义的智能
指针
; :拥有共享对象所有权语义的智能
指针
; :到 shared_ptr 所管理对象的弱引⽤; :拥有独有对象所有权语义的智能
指针
。 2.1 auto_ptr auto_ptr是通过由 new 表达式获得的对象,并在auto_ptr⾃⾝被销毁时删除该对象的智能
指针
,它可⽤于为动态分配的对象提供异常安 全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象,是⼀个轻量级的智能
指针
,适合⽤来管理⽣命周期⽐较短或者不会被 远距离传递的动态对象,最好是局限于某个函数内部或者是某个类的内部。 声明: template
&
lt; class T
&
gt; class auto_ptr; template
&
lt;
&
gt; class auto_ptr
&
lt;void
&
gt;; // 对类型void特化 成员函数: (1) : 获得内部对象的
指针
; (2) :释放被管理对象的所有权,将内部
指针
置为空,返回内部对象的
指针
,此
指针
需要⼿动释放; (3) :销毁内部对象并接受新的对象的所有权; (4) :从另⼀auto_ptr转移所有权; (5) 和:访问被管理对象。 注意事项: (1) 其构造函数被声明为explicit,因此不能使⽤
赋值
运算符对其
赋值
,即不能使⽤类似这样的形式 auto_ptr
&
lt;int
&
gt; p = new int; (2) auto_ptr 的对象所有权是独占性的,使⽤拷贝构造和
赋值
操作
符时,会造成对象所有权的转移,被拷贝对象在拷贝过程中被修改; (3) 基于第⼆条,因此不能将auto_ptr放⼊到标准容器中或作为容器的成员; (4) auto_ptr不能指向数组,释放时⽆法确定是数组
指针
还是普通
指针
; (5) 不能把⼀个原⽣
指针
交给两个智能
指针
对象管理,对其它智能
指针
也是如此。 auto_ptr是最早期的智能
指针
,在C++11 中已被弃⽤,C++17 中移除,建议使⽤unique_ptr代替auto_ptr。 简单实现: 1 template
&
lt;class T
&
gt; 2 class AutoPointer 3 { 4 public: 5 AutoPointer(T* ptr) 6 :mPointer(ptr){} 7 8 AutoPointer(AutoPointer
&
lt;T
&
gt;
&
amp; other) 9 { 10 mPointer= other.mPointer; //管理权进⾏转移 11 other.mPointer= NULL; 12 } 13 14 AutoPointer
&
amp; operator = (AutoPointer
&
lt;T
&
gt;
&
amp; other) 15 { 16 if(this !=
&
amp;other) 17 { 18 delete mPointer; 19 mPointer = other.mPointer; //管理权进⾏转移 20 other.mPointer= NULL; 21 } 22 23 return *this; 24 } 25 26 ~AutoPointer() 27 { 28 delete mP
一、智能
指针
在C++语言编程时,当类中有
指针
成员时,一般有两种方式来管理
指针
成员:一是采用值型的方式管理,每个类对象都保留一份
指针
指向的对象的拷贝;另一种更优雅的方式是使用智能
指针
,从而实现
指针
指向的对象的共享。
智能
指针
(smartpointer)的一种通用实现技术是使用引用计数(referencecount)。智能
指针
类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一
指针
。
每次创建类的新对象时,初始化
指针
并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝
指针
并增加与之相应的引用计数;对一个对象进行
赋值
时,
赋值
操作
符减少左
操作
数
C++智能
指针
原理 C++智能
指针
1. 智能
指针
原理 采⽤C++ Primer Plus中作者引出智能
指针
的⽅式进⾏说明,感觉超好。 ⾸先看2个函数: //函数1 void remodel(std::string
&
amp; str) { std::string * ps = new std::string(str); ... str = ps; return; } //函数2 void remodel(std::string
&
amp;str) { std::string * ps = new std::string(str); ... if (weird_thing()) throw exception(); str = *ps; delete ps; return; } 上⾯,函数1每次调⽤都在堆中申请内存,但从未收回,铁定内存泄漏;函数2虽然使⽤了异常,但是代码中出现异常时,delete将不执 ⾏,因此也仍会导致内存泄漏。 分析⼀下问题在哪? 其实不论上⾯remodel()函数是正常终⽌还是异常终⽌的,函数中的
指针
变量ps⾃⼰占据的内存都将从栈内存中删除,⽽ps指向的新申请到 的堆内存却没有被释放,导致了内存泄漏。所以啊,我们就在想当
指针
ps⾃⼰占据的内存被释放的时候,它指向的内存也被释放该多好啊。 但问题是ps现在是⼀个常规
指针
,它⽆法做到这⼀点。那C++中我们想的这件事情谁能帮我们做到呢?那就是类对象的析构函数,当ps是 ⼀个类对象的时候,在ps过期时,它的析构函数释放掉它所指的内存不就完成我们上⾯想要的事情了嘛!这正是C++智能
指针
的原理!! 这个原理就是:⽤具有析构函数的类对象充当
指针
,当该
指针
过期时,它的析构函数释放掉它所指的堆内存。 2. C++中4种智能
指针
模版类的区别 C++中4种智能
指针
模版类:auto_ptr,unique_ptr,shared_ptr,weak_ptr。 这4种智能
指针
也全都是基于上⾯的原理实现的。当使⽤这些
指针
时,是不需要我们⼿动使⽤delete去释放
指针
所指内存的。 简单的使⽤实例:记得包含头⽂件memory #include
&
lt;memory
&
gt; void remodel(std::string
&
amp;str) { std::string * ps = new std::string(str); str = *ps; return; } 接下来思考⼀个问题,如果你基于上⾯智能
指针
的思想实现了⼀个智能
指针
模版类mysmart_ptr。假设使⽤它进⾏内存管理,看下⾯的赋 值语句: mysmart_ptr
&
lt;string
&
gt; ps (new string(
&
quot;I reigned lonely as a cloud.
&
quot;)); mysmart_ptr
&
lt;string
&
gt; vocation; vocation = ps; 上⾯ps和vocation将指向同⼀个string对象,这样的话,当程序结束时可能会出现删除同⼀个对象两次的现象
&
mdash;
&
mdash;⼀次是ps过期时,另⼀ 次是vocation过期的时候,如果想避免这样的问题的话,我们需要想⼀下怎么解决?其实这样的⽅法有多种: 1. 定义
赋值
运算符,使之执⾏深拷贝。这样两个
指针
将指向不同的对象,其中的⼀个对象是另⼀个对象的副本。 2. 建⽴所有权(ownership)概念,对于特定的对象,只能有⼀个智能
指针
可拥有它,这样只有拥有对象的智能
指针
的构造函数会删除该 对象。然后,让
赋值
操作
转让所有权。这就是⽤于auto_ptr和unique_ptr的策略,但unique_ptr的策略更严格。 3. 创建智能
指针
更⾼的
指针
,跟踪引⽤特定对象的智能
指针
数。这称为引⽤计数(reference counting)。例如,
赋值
时,计数将加1, ⽽
指针
过期时,计数将减1。仅当最后⼀个
指针
过期时,才调⽤delete。这是shared_ptr采⽤的策略。 当然,同样的策略也适⽤于复制构造函数。每种⽅法都有其⽤途。 auto_ptr和unique_ptr: 上⾯也提到了,⽆论是auto_ptr还是unique_ptr,都是基于所有权的概念去解决可能出现的删除⼀个对象两次的问题的。这种情况下会有 什么问题呢?先看auto_ptr,看⼀个⼩例⼦: auto_ptr
&
lt;string
&
gt; films[2] = { auto_ptr
&
lt;string
&
gt; (new string(
&
quot;Fowl Balls
&
quot;)), auto_ptr
&
lt;string
&
gt; (new string(
&
quot;Duck Walks
&
quot;)) }; auto_ptr
&
lt;string
&
gt; pwin; pwin = films[0];//注:films[0]对内存失去了所有权 cout
&
lt;
&
lt; films[0]
&
lt;
&
lt; endl;//注:会出现错误,因为film[0]指向的内存此时已经
——关于
指针
的一个问题
今天班上的一个学霸在学习数据结构的字符串的时候遇到了一个问题,这个问题就是他在写代码的时候,不管怎么样Main方法里就是不能够给
指针
赋值
,就算
赋值
了好像
指针
里啥都没有,更别说使用其他函数了。
首先声明一下:这个问题其实并不难,个...
首先,一个静态
指针
数组是一个固定大小的数组,其中每个元素都是一个指向某种类型的
指针
。静态数组和
指针
数组分别是两个不同的概念。
针对静态
指针
数组的初始化,我们需要指定每个元素的初始值,这些初始值也必须是
指针
类型。一般来说,静态
指针
数组的初始化可以采用以下两种方式:
1. 在定义数组时指定每个元素的初始值,例如:
int *arr[] = {NULL, NULL, NULL};
这里我们定义了一个长度为3的int类型
指针
数组,并将每个元素初始化为NULL。
2. 通过循环为每个元素
赋值
,例如:
int *arr[3];
for (int i = 0; i
&
lt; 3; ++i) {
arr[i] = NULL;
这里我们首先创建了一个长度为3的数组,然后通过循环来为每个元素
赋值
为NULL。
总之,在使用静态
指针
数组时,我们需要特别注意其初始化,以确保每个元素都被正确地初始化为
指针
类型,从而避免出现不必要的错误。