C++程序开发技巧

这篇具有很好参考价值的文章主要介绍了C++程序开发技巧。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

类(class)的使用分为两种——基于对象(object Based)和面向对象(object oriented)

基于对象是指,程序设计中单一的类,和其他类没有任何关系

单一的类又分为:不带指针的类(class without pointer members)和带指针的类(class with pointer members)

面向对象则是类(class)中涉及了类之间的关系:复合(composition)、委托(delegation)、继承(inheritance)

1.头文件的防御式声明

#ifndef xxx
#define xxx
...
#endif

在编写头文件时应该有这样的一种习惯

目的是避免多次重复包含同一个头文件,否则会引起变量及类的重复定义

2.使用初始化列表的好处

  • 只有构造函数这类函数具有“初始化列表”这一特性
  • 从结果上来看,构造函数时使用初始化列表和在类内赋值是一样的,但我们都知道一个变量必须先初始化然后才被赋值,而初始化列表顾名思义是只执行初始化这一步,在类内赋值时就要先初始化再被赋值,所以从执行效率上讲,使用初始化列表会更快,也更简洁

3.设计模式:singleton(单例类)

  • demo:
class A {
public:
    static A & getInstance();
    setup() {...}
private:
    A();
    A(const A & rhs);
    ...
}

A & A::getInstance()
{
    static A a;
    return a;
}

...
//外部接口
A::getInstance().setup();
  • 原理:将构造函数设置为私有属性,同时设置一个静态函数接口返回一个该类对象

  • 作用:保证每一个类仅有一个实例,并为它提供一个全局访问点

  • 单例模式(Singleton)的主要特点不是根据用户程序调用生成一个新的实例,而是控制某个类型的实例唯一性。它拥有一个私有构造函数,这确保用户无法通过new直接实例它。除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance()方法负责检验并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

    这种模式主要有以下特征或条件:

    1. 有一个私有的无参构造函数,这可以防止其他类实例化它,而且单例类也不应该被继承,如果单例类允许继承那么每个子类都可以创建实例,这就违背了Singleton模式“唯一实例”的初衷。
    2. 单例类被定义为sealed,就像前面提到的该类不应该被继承,所以为了保险起见可以把该类定义成不允许派生,但没有要求一定要这样定义。
    3. 一个静态的变量用来保存单实例的引用。
    4. 一个公有的静态方法用来获取单实例的引用,如果实例为null即创建一个。
  • 参考:

    设计模式详解:Singleton(单例类)_singleton类_p_帽子戏法的博客-CSDN博客

    单例模式(Singleton)的6种实现 - JK_Rush - 博客园 (cnblogs.com)

4.常成员函数的重要性

  • 如果一个成员函数不改变类的数据成员时,就把它声明为常函数,这是一个好的习惯

  • 当实例化一个常对象时,常对象要求不能改变数据成员,如果成员函数不加const,将无法调用此成员函数,编译器不会通过,即使此函数确实没有改变数据成员;同时,即使成员函数被声明为了常函数,实例化一个普通对象时依然可以调用。

  • 简单来说,不声明为常成员函数可能不会有问题,但声明为常成员函数能确保一定不出问题

5.如何解释成员函数接收同类对象参数...

  • 问:如何解释一个类的成员函数在接收同类对象的参数(比如拷贝构造函数)可以直接调用该对象的任何成员,明明既不是友元也不是嵌套?

  • 答:相同class的各个objects互为friends(友元)

6.设计一个类要考虑什么

  • 目的:高效、安全、简洁、严密
  • 1.数据成员私有
  • 2.参数传递和返回值优先考虑用引用(传递的是地址值,这样不管传递的数据内存占用多大,依然固定传入四个字节,即使当传递字符这样小于四个字节时用值传递确实比引用或指针传递更快一些,但不必考虑这些细枝末节)
  • 3.构造函数优先去使用初始化列表
  • 4.能声明为常成员就声明为常成员

7.返回值加不加引用

  • 取决于返回的值是否要改变、是否可以改变,前者由我们决定,后者由语法限制

两个案例

class A
{
    int value;
    ...
};

...
    
A& fun1(A* x, const A& y)
{
    x.value += y.value; //第一参数会改变,第二参数不会改变
    return *x
}
class B
{
    int value;
    ...
};

...

B fun2(const B& x, const B& y)
{
	//第一参数和第二参数都不会改变
	return B(x.value + y.value);
}

8.运算符重载成员函数的思考

e.x.

inline complex&
_doapl(comlex* ths, const complex& r)
{
	...
	return *ths;
}

inline complex&
complex::operator += (const comlex& r)
{
	return _doapl(this, r);
}

...

comlex c1(2,1), c2(3), c3;
//c2 += c1;
//c3 += c2 += c1;

当重载一个二元运算符为成员函数时,我们知道重载函数除了右操作数是我们传递的,函数还会默认用一个this指针,来接收左操作数

那么可能会有疑问,我们想要改变的是左操作数,而且由于传递的是指针,函数内也确实可以改变,那返回值又有什么用呢,声明为空不就行了。

当我们使用重载运算符时只是像被注释的第一行代码一样,那么返回值确实不重要,但是当我们使用的形式像被注释的第二行代码时,那么返回值就很重要,因为c2.+=(c1) 这个函数的返回值就是 c3 += () 函数的参数

9.temp object(临时对象)

  • 语法: typename ();
  • 生存期:仅声明那一行
  • 返回值是临时对象时不难return by reference

10.<<重载的一些注意点

  • 只能重载为非成员函数

  • 左操作数固定为系统定义的 ostream 类型,且为非常量引用

    • 非常量:向流写入内容其实就是改变了它的状态
    • 引用:无法复制一个 ostream 对象
    • 注:ostream 类与 istream 类一样,它的的拷贝构造函数和赋值函数都是保护类型的,所以 ostream 是不允许拷贝或者赋值的,所以它也不能直接作为返回类型和参数传递,很多时候需要使用引用来进行传递。
  • 最好加返回值且为引用,原因前面已经说明,且我们对于连续调用<<的频率要大得多

  • 连续调用时的调用顺序

    complex c1, c2;
    cout << c1 << c2;
    //先执行 <<(cout, c1) 函数
    //返回的 ostream类型的cout的引用 又作为<<(ostream &, c2)的第一参数 
    
  • 在语法上我们当然也可以重载为成员函数,只要左操作数为自定义类型即可,但这样并不符合我们通常的书写习惯

C++程序开发技巧

11.Big Three(三位一体原则)

  • 三大件:拷贝构造、拷贝赋值、析构函数

  • 解释:当一个类需要我们去主动设计析构函数时,那它很大概率也需要一个拷贝构造函数和赋值运算符重载成员函数

  • 应用:当一个类具有指针成员时(class with point member)或者说当我们设计了一个有动态内存管理的类时

  • 原因:

    • 析构函数角度:默认析构函数会仅删除指向对象的指针,而删除一个指针不会释放指针指向对象占用的内存,最终会导致内存泄露

    • 拷贝构造角度:默认的构造函数是浅拷贝,复制的只是指针也就是地址值,这样导致两个对象共享一个内存空间,这是十分危险的,当其中一个对象被删除后,析构函数将释放那片共享的内存空间,接下来对这片已经释放了内存的任何引用都将会导致不可遇见的后果。

    • 赋值运算角度:

      赋值相比于拷贝构造要考虑更多

      首先是自我赋值判断,如果不判断,当左右操作数指向的是同一个地址时,会造成将左操作数对象的元素删除并释放其占用的内存,同时由于左右操作数指向同一对象,导致右操作数同时被删除,但接下来还要将右操作对象复制,这会造成不可预知的结果。这也被称为证同测试。

      其次是进行三步必要操作:

      • 释放已有内存
      • 开辟新的内存
      • 内容赋值
  • 代码示例:

#ifndef __MYSTRING__
#define __MYSTRING__

class String
{
public:                                 
   String(const char* cstr=0);//构造函数
   String(const String& str);//拷贝构造函数           
   String& operator=(const String& str);//重载=运算符 
   ~String();//析构函数                               
   char* get_c_str() const { return m_data; }//成员函数,返回指向字符数组首地址的指针
private:
   char* m_data;//字符数组指针
};

#include <cstring>

//构造函数
inline
String::String(const char* cstr)
{
   //开辟内存、计算长度、内容拷贝
   if (cstr) {
      m_data = new char[strlen(cstr)+1];
      strcpy(m_data, cstr);
   }
   else {   
      m_data = new char[1];
      *m_data = '\0';
   }
}

//析构函数
inline
String::~String()
{
   delete[] m_data;//释放指针指向空间
}

//重载=
inline
String& String::operator=(const String& str)
{
   //检测自我赋值(self assignment)
   if (this == &str)
      return *this;

   delete[] m_data;
   m_data = new char[ strlen(str.m_data) + 1 ];
   strcpy(m_data, str.m_data);
   return *this;
}

//构造函数
inline
String::String(const String& str)
{
   m_data = new char[ strlen(str.m_data) + 1 ];
   strcpy(m_data, str.m_data);
}

#include <iostream>
using namespace std;

//重载<<
ostream& operator<<(ostream& os, const String& str)
{
   os << str.get_c_str();
   return os;
}

#endif

12.new和构造函数,delete和析构函数

C++程序开发技巧

  • 当我们使用new创建了一个指向类的对象的指针时

    这里的new干了三件事:

    • 调用 operator new 函数,这个函数内部又调用了malloc函数来分配内存
    • operator new 函数返回的是空指针,显式转换为类类型指针后赋值给我们创建的指针
    • 调用指针指向对象的构造函数

C++程序开发技巧

  • 当我们使用delete释放一个指向对象的指针时
    • 首先调用对象的析构函数,这里该类的析构函数又用delete释放了类内动态分配的数组指针
    • 然后再释放这个指向对象的指针
    • 结合本例来看,就是delete ps,先delete它指向的成员,再delete它自己

13.malloc()动态分配内存的结构

C++程序开发技巧

  • 红色部分是 cookie ,记录内存分配的总大小,就是图中的41,其最低位用于表示是否已分配(1表示已分配,0表示已回收),之所以最低位可以变,是因为分配的内存总空间一定是16的倍数,其16进制表示时最低位一定为0,也就是说这个位置是空出来的,刚好用来表示内存状态。每一个 new 的对象都会有上下两个 cookie,来预先申请一块内存池,然后供对象实例化。

  • 绿色部分是调用malloc()时向系统申请的内存,该函数返回时,也会返回这块区域开头的指针。

  • 绿色部分上下两块 gap 预先被填充为了0xfdfdfdfd,用来分隔客户可以使用的内存区和不可使用的内存区,同时,当这块内存被归还时,编辑器也可以通过下gap的值区判断当前内存块是否被越界使用了

  • 从gap向上连续的7个内存空间共同组成了debug header,从上向下标号为1-7

    • 1、2两块空间保存了两根指针,目的是使多个内存块连接成链表。
    • 3空间保存了申请本内存块的文件名
    • 4空间保存了申请本内存块的代码行数
    • 5空间记录了本内存块中实际可以被用户使用的内存空间的大小
    • 6空间记录了当前内存块的流水号,即是链表中的第几个,从1开始
    • 7空间记录了当前内存块被分配的形式
  • 填补区pad

参考:

https://zhuanlan.zhihu.com/p/492161361

https://www.cnblogs.com/zyb993963526/p/15682014.html#_label2

https://blog.csdn.net/qq_61500888/article/details/122170203

14.array new 搭配 array delete

C++程序开发技巧

动态分配数组时要注意的:

  • 其内存区域相比上面所提到的多了一个内存块用来记录数组长度(分配对象数量)
  • 当申请内存后,返回的指针指向数据开始处,而使用 delete[] 释放时,指针会指向它的上一块,也就是记录数组长度的那一块,从而可以根据对象的数量调用相应次数的析构函数。如果使用 delete 释放的话,它不会去获取对象的长度,而是只调用指针指向的那一个对象的析构函数。
  • 如果对象的类型是内置类型或者是无自定义的析构函数的类类型,是可以使用 delete 来释放 new[] 对象的。但是,如若不然,使用 delete 来释放对象,对象所分配的内存空间虽然会照样全部释放,但是只会调用第一个对象的析构函数,这就导致内存泄漏。所以,养成良好的习惯,new [] 必 delete []

C++程序开发技巧

15.this指针

类的每一个非静态成员函数(包括构造函数、拷贝构造等)都隐含着一个指针形参名为this,当对象调用成员函数时就会隐含传递该对象的地址给它,这也是为什么一个类的成员函数虽然只有一份但也会根据接收的消息不同产生不同的行为,而静态成员函数不隐含this指针,所以即使调用它的对象不同维护的依然是同一段代码

C++程序开发技巧

16.namespace

三个案例:

  • 使用using引入一个命名空间的全部
  • 使用using引入一个命名空间的个体
  • 不用using,使用时手动引入

C++程序开发技巧文章来源地址https://www.toymoban.com/news/detail-470998.html

到了这里,关于C++程序开发技巧的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序如何开发,调试技巧,对程序员小白来说太重要了

    简要介绍一下微信小程序开发的基本流程和一些常用组件,供你参考。在实际开发过程中,你可以根据需求选择合适的组件和 API 进行开发。 首先,你需要注册成为微信小程序开发者。访问微信公众平台(https://mp.weixin.qq.com/)并用你的微信号登录。进入“小程序”模块,按照

    2024年02月07日
    浏览(74)
  • ONVIF协议网络摄像机(IPC)客户端程序开发使用ONVIF框架代码(C++)生成静态库04-->Windows

    先说一下,为什么不像前面Linux生成动态库那样去开发Onvif。因为Onvif的源码是没有那些__declspec(dlleXPort)指令,所以当你导出dll时,你会发现没有xxx.lib文件产生,在windows下你就没办法隐式调用dll了,除非你显示调用dll,即在程序中使用LoadLibrary()一个一个将要用到的函数加

    2023年04月08日
    浏览(59)
  • Node+GitLab实现小程序CI系统,web前端开发面试技巧

    后端功能实现 环境依赖 实现上传小程序后台接口; 实现拉取git项目到本地的接口; 实现获取gitlab项目信息,分支及tag的接口; 实现切换分支及tag接口 实现项目编译打包的接口; 实现拉起开发者工具的接口; 环境依赖 在后端功能的实现上用了以下框架和模块 express (nod

    2024年04月15日
    浏览(41)
  • Python-Python高阶技巧:HTTP协议、静态Web服务器程序开发、循环接收客户端的连接请求

    当前版本号[20231114]。 版本 修改说明 20231114 初版 1.1 网址的概念 网址又称为URL,URL的英文全拼是(Uniform Resoure Locator),表达的意思是 统一资源定位符 ,通俗理解就是网络资源地址。 URL地址:https://www.itcast.com/18/1122/10/E178J2O4000189FH.html 1.2 URL的组成 域名 : IP地址的别名 ,它是用

    2024年02月04日
    浏览(65)
  • 巴斯勒相机基于SDK 二次开发C++程序

    巴斯勒相机在VS中如何进行连接调用呢(软触发调用) 1.查找巴斯勒相机网口; 2.调用巴斯勒相机提供的接口,连接巴斯勒相机; 3.初始化相机; (安装巴斯勒相机软件) 其他引用第三方库以及对应头文件 链接:https://pan.quark.cn/s/71bb1b3e0dad 提取码:JfM9

    2024年04月14日
    浏览(60)
  • 《Qt 6 C++开发指南》提供4个版本的示例程序

    《Qt 6 C++开发指南》包含丰富的示例项目,为了方便读者使用《Qt 6 C++开发指南》学习Qt编程,本书提供了4个版本的示例程序。读者可在人民邮电出版社异步社区本书的配套资源(如图1)里下载这4个版本的示例程序。 图1 异步社区本书配套资源下载界面 这4个版本的关系如图

    2024年02月08日
    浏览(34)
  • 【微信小程序-原生开发】实用教程22 - 绘制图表(引入 echarts,含图表的懒加载-获取到数据后再渲染图表,多图表加载等技巧)

    微信小程序中使用 echarts 需使用官方提供的 ec-canvas 组件 点击下方链接,下载 ec-canvas 组件 https://gitcode.net/mirrors/ecomfe/echarts-for-weixin/-/tree/master 将其中的 ec-canvas 文件夹拷贝到微信小程序的分包中 ( 因 ec-canvas 组件较大,约 1M,若放在主包中很容易超出 2M 的大小限制,不了解

    2024年02月09日
    浏览(55)
  • C++界面开发框架Qt 6.x入门指南 - 拥有程序主窗口

    Qt技术交流群:166830288      欢迎一起进群讨论 点击获取Qt组件下载 Qt Widget 是桌面环境中典型的用户界面元素,这些小部件很好地集成到底层平台,在 Windows、Linux 和 macOS 上提供原生外观。 这些小部件成熟且具有丰富的用户界面元素,适用于大多数传统用户界面。 与 

    2024年02月05日
    浏览(47)
  • 16-4_Qt 5.9 C++开发指南_Qt 应用程序的发布

    用 Qt 开发一个应用程序后,将应用程序提供给用户在其他计算机上使用就是应用程序的发布。应用程序发布一般会提供一个安装程序,将应用程序的可执行文件及需要的运行库安装到用户计算机上,即使用户计算机上没有安装 Qt 也能正常运行安装的程序。 Qt的应用程序发布

    2024年02月14日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包