C++ STL开发温习与总结(三): 3.C++函数技术

简介: C++ STL开发温习与总结(三): 3.C++函数技术

若该文为原创文章,转载请注明原文出处

本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78072366

长期持续带来更多项目与技术分享,咨询请加QQ:21497936、微信:yangsir198808

红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...

其他(编程相关)

上一篇:C++ STL开发温习与总结(二): 2.C++存储技术

下一篇:C++ STL开发温习与总结(四): 4.C++面向对象机制的实现

 

前言

      几乎所有的C++类都有一个或多个构造函数,一个析构函数和一个赋值操作符。

      对于任何一个类A,如果不想编写上述函数,C++编译器将自动为A产生4个缺省的函数,如下:

       A(void);// 缺省的无参数构造函数
       A(constA &a); // 缺省的拷贝构造函数
       ~A(void);//缺省的析构函数
       A &operate = (const A & a); // 缺省的赋值函数

 

1 类的构造函数、析构函数与赋值函数

      C++提供了更好的机制来增强程序的安全性。C++编译器具有严格的类型安全检查功能,它几乎能找出程序中所有的语法问题。

      根据经验,不少难以察觉的程序是由于变量没有被正确初始化或清楚造成的,而初始化和清楚工作很容易被人遗忘。C++很好的考虑了这个问题,把对象的初始化工作放在构造函数中,把清除工作放在析构函数中。

1-1 构造函数技术

      构造函数除了具有普通成员函数的所有特性外还有下面的几个特殊点:

l  构造函数名必须和类名相同,且不得声明任何返回值类型;

l  构造函数可以用形参形式带进各成员数据的初值,也可以重载多个构造函数;

l  构造函数特殊的功能:测算本类对象所需的静态内存容量并动态占用该容量的内存资源;

l  构造函数并非没有返回值,它所返回的值是一个指向其所定义对象的首地址的指针,但不能被传递;

l  若直接引用声明于public区中的构造函数将导致该类的一个新对象生成,但若没有相关的句柄与之来年系,则无法与对象进行通信联系;

l  生命与private区中的构造函数只能通过定义在public区中的函数引用;

l  如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数;

l  类的const常量只能在初始化表里被初始化,它不能在函数体内用赋值的方式来初始化;

l  类的数据成员的初始化可以采用初始化表或函数体内赋值两种方式。非内部数据类型的成员对象应当采用第一种方式初始化,以获取更高的效率。

缺省构造使用:

Demo h;

非缺省构造使用:

 

 

Demoh(0);// 或者写成Demoh=4;

追求更高的效率

B::B(const A &a) : m_a(a) { …}

同样的功能,下面先暗地里创建m_a,再调类A的赋值函数

B::B(const A &a) { m_a = a; …};

1-2 析构函数技术

    析构函数由定义类对象的分程序在其运行结束之前被自动地引用。与构造函数一样,析构函数也不得声明任何的返回类值型、不得带有任何参数且不得重载,也不能被置于private区,有且仅有一个析构函数。

      析构函数除了可以(用delete)释放在构造函数中指明占用的内存外,还要释放整个对象的成员等所占用的内存,这些只是不必写进去。

      如果一个基类的析构函数被说明未虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它是否引用了关键字virtual进行说明。

1-3 构造和析构的次序

      构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对像的构造函数。      析构则严格按照与构造相反的次数执行,该次序是惟一的,否则编译器将无法自动执行析构过程。

    一个特别的现象是:成员对象初始化的次序完全不受它们在初始化表中次序的影响,只由成员对象在类中声明的次序状态决定,这是因为类的声明是惟一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。

1-4 成员初始化表设计

      C++语言在原有赋值方式的基础上,增加了一个成为成员初始化表的特别手段来简化这种初始化过程,由于此种手段是编译阶段由编译器将要初始化的成员数据与参数建立了对应联系,所以用此法的系统在运行阶段的开销较之其他方法都要小得多

      成员初始化表放在构造函数名与构造函数体之间,用冒号与函数名部分相分隔,每个表的格式为:

      类中成员名(初值),类中成员名(初值)

ST(unsignedint y, unsigned int, unsigned int d): yy(y), mm(m), dd(d)

      C++语言中只有const和引用类型是要在编译时就要指明其初值。由于定义在类中的成员都是抽象的数据结构描述,不可能分配内存单元,因此在对构造函数进行编译的阶段也就不可能完成赋初值的操作了。为了解决这一矛盾,C++语言只有借助于类对象“成员初始化表”的描述去将要赋初值的成员名及初值预先声明,待执行时产生对象(即分配了内存单元)后再补做上述的赋初值操作。所以从某种意义上讲,成员初始化表是特意为这两种数据成员准备的也不为过,由此切记成员初始化表不可写在声明语句上。

1-5 类中的const成员函数的特性

* 防止本函数误写参数变量

返回值类型函数名(const 参数, const 参数, …)

* 防止本函数误写类对象内全部的变量的

返回值类型函数名(参数表) const;

* 防止其他函数误写返回地址或引用的

const 返回值类型函数名(参数表)

1-6 拷贝构造函数与赋值函数

      由于并非所有的对象都会使用拷贝函数和赋值函数,程序员可能对这两个函数有些轻视(确实如此)。如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。

      以类String的两个对象a,b为例,假设a.m_data的内容为“hello”,b.m_data的内容为“world”。现在a赋值给b,缺省赋值函数的“位拷贝”意味着执行b.m_data=a.m_data。这将造成3个错误:

      一是b.m_data原来的内存没有被释放,造成内存泄漏;

      二是b.m_data和a.m_data指向同一块内存,a或b任何一方变动都将影响另一方;

      三是在对象被析构时,m_data被释放了两次。

 

2 在派生类中实现类的基本函数

      基类的构造函数、析构函数、赋值函数都不能被派生类继承。如果类之间存在继承关系,在编写上述基本函数时应注意以下事项:

n  派生类的构造函数应在其初始化表里调用积累的构造函数

n  基类与派生类的析构函数应为虚(即加virtual关键字)。

3 内联函数技术

      C++语言支持函数内联,其目的是为了提高函数的执行效率(速率)。在C程序中,可以用宏代码提高执行效率。宏代码本身不是函数,但使用起来湘函数。与处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程,从而提高了速率。使用宏代码最大的缺点是容易出错,与处理器在复制宏代码时常常产生意想不到的边际效应。例如:

#defineMAX(a,b)  (a)>(b)?(a):(b)

可改写避免边际效应

#defineMAX(a,b)  ((a)>(b)?(a):(b))

      函数调用会带来降低效率的问题,因为调用函数实际上将程序执行顺序转移到函数所存放的内存中的某个地址,将函数的程序内容执行完后,在返回到调用该函数前程序语句所在位置。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后要先恢复现场,并按原来保存地址继续执行。因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。特别是对于一些函数体代码不是很大,但又频繁地被调用的函数来说,解决其效率问题更为重要。引入内联实际上是为了解决这一问题。

*  在内联函数不允许用循环语句和开关语句

*  内联函数的定义必须出现在内联函数第一次被调用之前。

 

4友元函数技术

      只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

      为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明是前面加关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率,但是,它破坏了封装性和隐藏性,使得非成员函数可以访问类的私有成员。

      友元可以是一个函数,也可以是一个类。

 

上一篇:C++ STL开发温习与总结(二): 2.C++存储技术

下一篇:C++ STL开发温习与总结(四): 4.C++面向对象机制的实现

 

若该文为原创文章,转载请注明原文出处

本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78072366



相关文章
|
6月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
168 2
|
6月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
318 73
|
3月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
83 0
|
7月前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
6月前
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
256 3
|
6月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
322 6
|
7月前
|
存储 算法 C++
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
318 1
|
8月前
|
C++ 容器
【c++丨STL】stack和queue的使用及模拟实现
本文介绍了STL中的两个重要容器适配器:栈(stack)和队列(queue)。容器适配器是在已有容器基础上添加新特性或功能的结构,如栈基于顺序表或链表限制操作实现。文章详细讲解了stack和queue的主要成员函数(empty、size、top/front/back、push/pop、swap),并提供了使用示例和模拟实现代码。通过这些内容,读者可以更好地理解这两种数据结构的工作原理及其实现方法。最后,作者鼓励读者点赞支持。 总结:本文深入浅出地讲解了STL中stack和queue的使用方法及其模拟实现,帮助读者掌握这两种容器适配器的特性和应用场景。
176 21
|
7月前
|
存储 算法 C++
深入浅出 C++ STL:解锁高效编程的秘密武器
C++ 标准模板库(STL)是现代 C++ 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C++ 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知识、发展历史、核心组件、重要性和学习方法。
|
7月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。