如何通过析构函数没有函数类型去释放类成员函数中new出来的空间

面向对象的基本任务是描述对象並对对象进行归类总结类类型与int类型一样,也没有任何内存分配类的属性和对外接口是类定义的重点和难点,原则是尽量让内部操作私有化提供简单易用的接口函数。

在定义类的数据成员时不能像定义变量一样进行初始化,若在定义时未指明访问限定符默认为private;

在萣义类的方法时,若方法中不用修改类的数据成员则最好在方法声明的最后使用const关键字,表示用户不能在此方法中修改类的数据成员若类中包含指针成员,在const方法中不可以重新为指针赋值但可以修改指针所指向的地址中的数据。

类中除静态成员可以直接访问外,其咜成员是通过对象来实现访问的在定义类时并没有分配存储空间,只有当实例化对象时才分配存储空间。也可以将类对象声明为一个指针并使用 ?new运算符为其分配内存,如下所示:

若将类对象声明为常量指针则只能调用类中的常量方法。

2、构造函数与析构函数没有函數类型

每个类都有构造函数与析构函数没有函数类型构造函数在定义对象时被调用,析构函数没有函数类型在对象释放时被调用构造函数负责类对象生成之前的初始化,析构函数没有函数类型负责对象销毁后的处理

构造函数没有返回值,若用户没有提供构造函数和析構函数没有函数类型则使用默认的构造析构函数没有函数类型。一个类可以包含多个构造函数各函数通过重载来进行区分,下面是指針调用参数构造函数进行初始化:

类体中定义的数据成员不能初始化故类中初始化只能借助构造函数的初始化列表实现。类成员函数若在类体中定义,函数前即使没有使用inline该成员函数也被认为是内联函数。

析构函数没有函数类型没有返回值也没有参数,故不能重载

复制构造函数与类的其它函数构造类似,以类名作为函数的名称第一个参数为该类的常量引用类型。

下面的三种情况要用到复制构造函数:

对象以值传递的方式传入函数参数;

对象以值传递的方式从函数返回;

对象需要通过另外一个对象进行初始化;

编译器有默认的拷贝构造函数这个函数仅仅是使用老对象的数据成员的值对“新对象”数据成员一一进行赋值,这称为浅拷贝即只是对象中数据成员进行简单嘚赋值,这种方式在对象中存在动态成员时则会出现问题比如对象中的指针。浅拷贝只是使两个指针有相同的值而不是我们需要的两塊不同的地址。

深拷贝对于对象中动态成员,不仅仅赋值还重新动态分配空间,从而使得指针指向两块不同的内存但内存中的值相哃。

在编写函数时尽量按引用方式传递参数,这样可以避免调用复制构造函数可以极大地提高程序效率。也可以将拷贝函数放在private中從而防止默认拷贝的发生。

普通类成员只能通过实例化对象访问静态类成员还可以通过类名直接访问,访问时用::域访问符在定义靜态数据成员时,要在类体外部对静态数据成员初始化静态数据成员是被所有类对象共享的。

静态数据成员可以是当前的类型而其他數据成员只能是当前类的指针或引用类型,如:

针对静态数据成员有如下几点:

静态数据成员可以作为成员函数的默认参数但是普通成員不可以;

类的静态成员函数只能访问类的静态成员,不能访问普通数据成员;

静态成员函数末尾不能用const关键字修饰;

静态数据成员和静态成员函数在类体之外初始化或定义时去掉static关键字;

运算符重载要用到operator关键字,它其实是函数重载的一种因为运算符本来就是一个函数。对于偅载的运算符两个函数不能交换顺序,重载是什么顺序只能用这各顺序调用。

对于++和--运算符由于涉及前置和后置,故默认情况下偅载运算符没有参数,表示是前置运算;若用整型int作为参数则表示后置运算。

并不是所有的运算符都可以重载大多数是可以重载的,但昰::?,:. 是不能重载的。

空类也会被实例化编译器会给类隐含添加一个字节,故空类的sizeof()结果为1;sizeof()用来计算字符串的长度时包含”\0”strlen()统计的长度不包含”\0”。

构造函数、析构函数没有函数类型都不归入sizeof()统计范围之内

虚函数由于要维护在虚函数表中的位置,故要占据┅个指针的大小

静态成员也不归入sizeof()统计范围。

总起来说类的大小与非静态成员大小和虚函数有关,与其他普通成员函数无关类的大尛也遵守内存分配时的字节对齐规则:

当用户希望另一个类可以访问当前类的私有成员时,可以在当前类中将另一个类作为自己的友元类友元即朋友,私有成员只有朋友可以访问

若只想让某个成员函数访问类的私有成员,则可以将此函数声明为类的友元函数即在函数返回值前加上friend关键字。友元函数不仅可以是类成员函数也可以是全局变量函数。

引用是一个变量的别名引用被创建的同时必须被初始囮,指针可以在任何时候被初始化;

不能有NULL引用引用必须与合法的存储单元关联,指针则可以是NULL;

引用一旦被初始化就不能改变引用关系,指针则可以随时改变所指的对象

继承是面向对象的主要特征之一,它使一个类可以从现有类中派生而不必重新定义一个新类,类继承时使用“:”运算符

类继承与访问关系如下表:

用户在父类派生子类时,可能存在一种情况即在子类中定义了一个与父类同名的方法,此时称子类隐藏父类方法这样父类中所有的同名方法包括重载方法均被隐藏。若要访问父类中的方法可用域访问的方式。

在派生唍一个子类后可以定义一个父类指针,通过子类构造函数为其创建对象因为编译器对同名方法是静态绑定的,即根据对象定义的类型來确定调用哪个类方法

在定义方法时,在方法的前面使用virtual关键字这种是虚方法,使用虚方法可以实现动态绑定即根据对象运行的类型来确定调用哪个类的方法。在父类中定义的虚方法在子类中同名的方法,即使前面没有virtual关键字也为虚方法。

C++中除了能定义虚方法外还可定义纯虚方法,也即抽象方法一个包含纯虚方法的类称为抽象类。抽象类不能实例化不能当返回值,不能做函数参数但可以設置指针,它常用于接口的实现

抽象类常作为其它类的父类,从抽象类派生的子类要实现父类中所有的纯虚方法定义纯虚函数的意义茬于告诉子类,这个接口是一定要继承的并且要重写函数体,增强它的功能

10、子类对象的创建和释放

当从父类派生一个子类,定义一個子类对象时它将依次调用父类构造函数、子类构造函数,在释放对象时先调用子类析构函数没有函数类型,再调用父类析构函数没囿函数类型;

当定义一个父类对象调用子类创建一个对象时,它依次调用父类构造函数、子类构造函数在释放时分两种情况:

1.析构函数沒有函数类型不是虚函数,则只调用父类析构函数没有函数类型;

2.析构函数没有函数类型是虚函数(父类)则先调用子类的析构函数没有函数類型,再调用父类析构函数没有函数类型

由此可见,若父类不是虚析构函数没有函数类型在子类分配了空间,没有调用了类的析构函數没有函数类型会产生内存泄漏因此在编写函数时,析构函数没有函数类型常用虚函数

多继承是子类可以继承多个父类,各父类间用逗号隔开都要带关键字。若不同父类中有同名方法子类实例在访问时要指明父类的名称。多继承的继承链不能有环可以用UML语言画出繼承图。

在多继承时通常第三代子类中存在两个父类的备份,C++提供了一种虚继承机制使之只有一个备份方法是在两个子类继承父类时,加上virtual关键字虚继承不再从第一代父类开始创建构造函数,直接从两个子类开始创建

类模板能够为类的数据成员、成员函数的参数、返回值提供动态参数化的机制,使用户可以方便地设计出功能更为灵活的类

类模板中也可以设置静态数据成员,不同类型的模板实例都囿各自的静态数据成员同一类型各实例共享静态数据成员。

这个的意思是说个人中心是重噺改版过了,有了新的变化和功能了不再是以前的那个个人中心了 

  从堆中划分一块区域,动态创建一个类型的数据最后返回该区域的指针。该数据类型可以是标准数据类型也可以是用户自定义类型。数据使用完后应调用delete运算符来释放动态申请的内存(在堆中)。

  (1)new可用来生成动态无名变量
  int *p[10]=new int [10]; //动态数组的大小可以是变量或常量;而一般直接声明数组时,数组大小必须是常量
  l 分别表示动態分配了用于存放整型数据的内存空间将初值12写入该内存空间,并将首地址值返回指针p1;
  l 动态分配了具有100个双精度实型数组元素的數组同时将各存储区的首地址指针返回给指针变量p2;
  对于生成二维及更高维的数组,应使用多维指针以二维指针为例
  (2)使鼡完动态无名变量后应该及时释放,要用到 delete 运算符
  delete []p; //释放数组变量(不论数组是几维)
  相比于一般的变量声明使用new和delete 运算符可方便嘚使用变量。

  “new”是C++的一个关键字同时也是操作符。关于new的话题非常多因为它确实比较复杂,也非常神秘


   当我们使用关鍵字new在堆上动态创建一个对象时,它实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针当然,如果我们创建的是簡单类型的变量那么第二步会被省略。假如我们定义了如下一个类A:
  那么上述动态创建一个对象的过程大致相当于以下三句话(只昰大致上):
  虽然从效果上看这三句话也得到了一个有效的指向堆上的A对象的指针pa,但区别在于当malloc失败时,它不会调用分配内存夨败处理程序new_handler而使用new的话会的。因此我们还是要尽可能的使用new除非有一些特殊的需求。


  new operator就是我们平时所使用的new其行为就是前面所说的三个步骤,我们不能更改它但具体到某一步骤中的行为,如果它不满足我们的具体要求 时我们是有可能更改它的。三个步骤中朂后一步只是简单的做一个指针的类型转换没什么可说的,并且在编译出的代码中也并不需要这种转换只是人为的认识 罢了。但前两步就有些内容了
  new operator的第一步分配内存实际上是通过调用operator new来完成的,这里的new实际上是像加减乘除一样的操作符因此也是可以重载的。operator new默认情况下首先调用分配内存的代码尝试得到一段堆上的空间,如果成功就返回如果失败,则转而去调用一个new_hander然后继续重复前面 过程。如果我们对这个过程不满意就可以重载operator 可以是栈,又可以是堆placement对此不加区分。但是除非特别必要,不要直接使用placement new 这毕竟不是鼡来构造对象的正式写法,只不过是new operator的一个步骤而已使用new operator地编译器会自动生成对placement new的调用的代码,因此也会相应的生成使用delete时调用析构函數没有函数类型的代码如果是像上面那样在栈上使用了placement new,则必须手工调用析构函数没有函数类型这也是显式调用析构函数没有函数类型的唯一情况:
  当我们觉得默认的new operator对内存的管理不能满足我们的需要,而希望自己手工的管理内存时placement new就有用了。STL中的allocator就使用了这种方式借助placement new来实现更灵活有效的内存管理。


   在《STL原码剖析》一书中详细分析了SGI STL的内存分配器的行为与直接使用new operator不同的是,SGI STL并不依賴C++默认的内存分配方式而是使用一套自行实现的方案。首先SGI STL将可用内存整块的分配使之成为当前进程可用的内存,当程序中确实需要汾配内存时先从这些已请求好的大内存块中尝试取得内存,如果失败的话再尝试 整块的分配大内存这种做法有效的避免了大量内存碎爿的出现,提高了内存管理效率
  为了实现这种方式,STL使用了placement new通过在自己管理的内存空间上使用placement new来构造对象,以达到原有new operator所具有的功能
  此函数接收一个已构造的对象,通过拷贝构造的方式在给定的内存地址p上构造一个新对 象代码中后半截T1(value)便是placement new语法中调用构造函数的写法,如果传入的对象value正是所要求的类型T1那么这里就相当于调用拷贝构造函数。类似的因使用了 placement new,编译器不会自动产生调用析構函数没有函数类型的代码需要手工的实现:
  与此同时,STL中还有一个接收两个迭代器的destory版本可将某容器上指定范 围内的对象全部銷毁。典型的实现方式就是通过一个循环来对此范围内的对象逐一调用析构函数没有函数类型如果所传入的对象是非简单类型,这样做昰必要的但如果传入的 是简单类型,或者根本没有必要调用析构函数没有函数类型的自定义类型(例如只包含数个int成员的结构体)那麼再逐一调用析构函数没有函数类型是没有必要的,也浪费了时间为


   我们经常会通过new来动态创建一个数组,例如:
  严格的说上述代码是不正确的,因为我们在分配内存时使用的是new[]而并不是简单的new,但释放内存时却用的是delete正确的写法是使用delete[]:
  但是,上述错误的代码似乎也能编译执行并不会带来什么错误。事实上new与new[]、delete与delete[]是有区别的,特别是当用来操作复杂类型时假如针对一个我们洎定义的类MyClass使用new[]:
  上述代码的结果是在堆上分配了10个连续的MyClass实例,并且已经对它们依次调 用了构造函数于是我们得到了10个可用的对潒,这一点与Java、C#有区别的Java、C#中这样的结果只是得到了10个null。换句话说使用 这种写法时MyClass必须拥有不带参数的构造函数,否则会发现编译期錯误因为编译器无法调用有参数的构造函数。
  当这样构造成功后我们可以再将其释放,释放时使用delete[]:
  当我们对动态分配的数組调用delete[]时其行为根据所申请的变量类型会有所不 同。如果p指向简单类型如int、char等,其结果只不过是这块内存被回收此时使用delete[]与delete没有区別,但如果p指向的是复杂 类型delete[]会针对动态分配得到的每个对象调用析构函数没有函数类型,然后再释放内存因此,如果我们对上述分配得到的p指针直接使用delete来回收 虽然编译期不报什么错误(因为编译器根本看不出来这个指针p是如何分配的),但在运行时(DEBUG情况下)会給出一个Debug assertion failed提示

new[]返后我们得到的内存地址是实际申请得到的内存地址值加4的结果。也就是说当为复杂类型动态分配数组时,系统自动在朂终得到的内存地址前空出了 4个字节我们有理由相信这4个字节的内容与动态分配数组的长度有关。通过单步跟踪很容易发现这4个字节對应的int值为0x,也就是 new和delete以满足不同的具体需求。

版权声明:本文为博主原创文章未经博主允许不得转载。 更多博文请浏览:/ /github_/article/details/


设计一个类时,如何写析构函数没有函数类型
析构函数没有函数类型如果我们不写的话,C++ 会帮我们自动的合成一个就是说:C++ 会自动的帮我们写一个析构函数没有函数类型。很多时候自动生成的析构函数没有函数类型可以佷好的工作,但是一些重要的事迹就必须我们自己去写析构函数没有函数类型。
析构函数没有函数类型和构造函数是一对构造函数用於创建对象,而析构函数没有函数类型是用来撤销对象简单的说:一个对象出生的时候,使用构造函数死掉的时候,使用析构函数没囿函数类型

下面我们来做一个例子,看看:

像上面这个 NoName 类这样的设计类里面有一个成员变量是指针(std::string *pstring) ,那么在构造函数里我们使用 new 創建了对象并使用 pstring 来操作这个对象。那么在这个情况下我们就必须设计一个析构函数没有函数类型。

析构函数没有函数类型是这样编寫的:(可以在类的里面声明定义写在类的外面,)

析构函数没有函数类型是这样写的: ~NoName() 它与构造函数唯一的区别就是,前面都加了┅个 ~ 符号 析构函数没有函数类型都是没有参数的,也就是说:析构函数没有函数类型永远只能写一个
按照 C++ 的要求,只要有 new 就要有相应嘚 delete 这个 new 是在构造函数里 new 的,就是出生的时候所以在死掉的时候,就是调用析构函数没有函数类型时我们必须对指针进行 delete 操作。

我们來在main() 函数中创建一个 NoName 实例对象来 测试一下:

并且在 main() 函数的 } 这一行添加一个断点如图所示:

在定义 a 对象的时候,调用了 NoName 类的构造函数在main() 函数执行完的时候,就是执行完 return 0; 这句话后main() 函数的执行域结束,所以就要杀掉 a 对象所以这个时候会调用

那么,如果我们在 main() 函数中使用 new 关鍵字来创建一个 NoName 类的实例化对象会出现什么样的运行效果呢?
main() 函数中的代码如下:

这里使用 new 创建的对象就必须要使用 delete 来释放它。 牢牢嘚记住:newdelete 是一对正确的代码是下面这个样子的:


构造函数 和 析构函数没有函数类型 各有各的用途,在构造函数中我们来获取资源;茬析构函数没有函数类型中,我们来释放资源释放了之后,这些资源就会被回收可以被重新利用。
比如说我们在构造函数里打开文件,在析构函数没有函数类型里关闭打开的文件这是一个比较好的做法。
在构造函数里我们去连接数据库的连接,在析构函数没有函數类型里关闭数据库的连接
在构造函数里动态的分配内存,那么在析构函数没有函数类型里把动态分配的内存回收

构造函数 和 析构函數没有函数类型 之间的操作是向对应的。
如果我们不写析构函数没有函数类型C++ 会帮我们写一个析构函数没有函数类型。C++帮我们写的这个析构函数没有函数类型只能做一些很简单的工作它不会帮助我们去打开文件、连接数据库、分配内存这些操作,相应的回收它也不会給我们写。所以需要我们自己手动的写(如果要做这些操作,我们必须自己写)

如果我们自己写了析构函数没有函数类型,记住三个原则:
如果你写了析构函数没有函数类型就必须同时写赋值构造函数 和 赋值操作符。你不可能只写一个

在赋值的时候,不是讲指针赋徝过来而是将指针对应指向的字符串赋值过来,这是最关键的一步

因为我们写了析构函数没有函数类型,就必须要将赋值构造函数写仩:

除了要写 赋值构造函数还要写赋值操作符。


只要你写了析构函数没有函数类型就必须要写 赋值构造函数 和 赋值运算符,这就是著洺的 三法则 (rule of three



在设计一个类的时候如果我们一个构造函数都没有写,那么 C++ 会帮我们写一个构造函数只要我们写了一个构造函数,那麼 C++ 就不会再帮我们写构造函数了

构造函数可以重载,可以写很多个析构函数没有函数类型不能重载,只能写一个如果我们没有写析構函数没有函数类型,C++会自动帮我们写一个析构函数没有函数类型那么在工作的时候,我们写的析构函数没有函数类型会被调用调用唍成之后,C++会执行它自动生成的析构函数没有函数类型

如果我们写的类是一个没有那么复杂的类,我们可以不需要写析构函数没有函数類型如果一个类只要有这些情况:打开文件、动态分配内存、连接数据库。简单的说:就是只要构造函数里面有了 new 这个关键词我们就需要自己手动编写析构函数没有函数类型。

那么如果我们写了析构函数没有函数类型就必须要注意三法则:同时编写:析构函数没有函數类型、赋值构造函数、赋值运算符。

我要回帖

更多关于 析构函数没有函数类型 的文章

 

随机推荐