第一次实践一个多文件的代码

2023-10-27

收获

1.#ifndef防止头文件重复包含

为了避免同一个头文件被包含(include)多次,C/C++中有两种宏实现方式:一种是#ifndef方式;另一种是#pragma once方式。


#ifndef 标识符A
//每一个头文件都要有自己独特的标识
//(有一定的规则,但是主要是自己定义的,看心情哈哈哈)
//这句话是在判断该头文件是否定义过,
//如果定义过,则执行#endif后面的内容
//没有定义过,则执行#define里面的内容执行定义

#define 标识符A

……//这里面写头文件的东西啦

#endif
......//假如头文件被定义过了,然后要干啥可以写在这里,一般没啥可干的

C语言中,常常一些头文件被多次包含(#include" "),这样就可能出现嵌套包含现象,比如a.h文件被包含进b.h文件,而a.h文件与b.h文件又被包含进c.h文件中,如此a.h文件在c.h文件中出现了两次,这样不仅影响预处理的效率,有时还会引发错误,所以我们想办法在a.h中做些标记,使得a.h被其他文件多次包含时只处理第一次。

这里的“标识符A”是自己定义的,但每一个文件里的该“标识符A”必须是唯一的。

而诸多老师为了方便辨认,以及宏名常用大写表示,
所以常将 “标识符A” 写成 “_头文件名大写_H” 。
实际上这里的“标识符A”的名称与头文件名称没有什么必然联系

(1)#ifndef

#ifndef的方式受C/C++语言标准支持。它不仅可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件(或者代码片段)不会被不小心同时包含。

当然,缺点就是如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,但编译器却硬说找不到声明的状况——这种情况有时非常让人郁闷。

由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,ifndef会使得编译时间相对较长,因此一些编译器逐渐开始支持#pragma once的方式。

(2)#pragma once

#pragma once 一般由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。

你无法对一个头文件中的一段代码作pragma once声明,而只能针对文件。

== 其好处是,你不必再担心宏名冲突了==,当然也就不会出现宏名冲突引发的奇怪问题。大型项目的编译速度也因此提高了一些。

对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名冲突引发的“找不到声明”的问题,这种重复包含很容易被发现并修正。

另外,这种方式不支持跨平台!


2. 关键字explicit防止隐式转化

C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生,声明为explicit的构造函数不能在隐式转换中使用。

C++中, 一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。

1 是个构造;2 是个默认且隐含的类型转换操作符。

所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候编译器就自动调用这个构造器, 创建一个AAA的对象。

这样看起来好象很酷, 很方便。 但在某些情况下, 却违背了程序员的本意。 这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用。

解析:explicit构造函数是用来防止隐式转换的。请看下面的代码:

#include <iostream>
using namespace std;
class Test1
{
public :
	Test1(int num):n(num){}
private:
	int n;
};
class Test2
{
public :
	explicit Test2(int num):n(num){}
private:
	int n;
};
 
int main()
{
	Test1 t1 = 12;
	Test2 t2(13);
	Test2 t3 = 14;
		
	return 0;
}

编译时,会指出 t3那一行error:无法从“int”转换为“Test2”。而t1却编译通过。注释掉t3那行,调试时,t1已被赋值成功。

注意:当类的声明和定义分别在两个文件中时,explicit只能写在在声明中,不能写在定义中。


3.override告诉编译器该函数用于重载父类的某个虚函数

例程
成员函数为虚函数
我们的意图是在子类Derived1与Derived2分别重载父类Base::print(void),
在我们下面的代码中有没有发现什么问题呢?

class Base {
public:
    virtual void print(void){...}
};
class Derived1 : public Base {
public:
    void print(void){...}
};
class Derived2 : public Base {
public:
    void Print(void){...}
};

额…,原来因为不小心,在Derived2中将print误输入为Print,(注意其中的字母p的大小写),关键问题是编译器完全可以正确的编译上面的代码,这是一个很难发现的错误。

问题来了: 如何简单直接的向编译器表明意图,我就是要重载Base::print(void)呢?如果我写错了字母,你(指代编译器)要直接告诉我错误,或者一个警告也行?

答: override关键字可以做这件事情。

代码修改如下:

class Base {
public:
    virtual void print(void){...}
};
class Derived1 : public Base {
public:
    void print(void) override {...}
};
class Derived2 : public Base {
public:
    void Print(void) override {...}
};

当编译器看到上面代码的时候就开始很不爽了,编译器开始抱怨了,你通过 “override” 告诉我 "Derived2::Print(void)是要重载父类的某个函数,可是我根本没有在父类中找到 "Print(void)"函数,编译器直接丢出个错误,然后甩膀子不干了。
你知道的,编译器都是很娇贵的,当他甩脸色的时候,你也只能乖乖的仔细看他给你的错误,哦,原来是不小心写错字母了,一切就这么完美的解决了。

总结
override用于直接明了的告诉编译器该函数用于重载父类的某个虚函数

另外:虚函数的子函数前面加不加vitual都可以
因为:

c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此,在子类从新声明该虚函数时,可以加,也可以不加,但习惯上每一层声明函数时都加virtual,使程序更加清晰。


4. 大括号初始化

空大括号赋值会创建一个值初始化的临时量并赋值给赋值对象 以保证空间存在
vector vi{1, 2, 3, 4, 5};

接下来是赋值的情况:

vector vi;

vi = {6, 7, 8, 9, 10};

这种形式,对于有限多个数值的赋值是非常有用的。

补充

和初始化一样,使用大括号包围的值列表也有同样的优势。一个是防止窄化,有时可以简单的理解为防止精度降低,例如下面的代码是无法编译通过的:

double pai = 3.1415926;

int pi;

pi = {pai}; //编译错误。

另外,如果大括号里的初始化列表为空,编译器会创建一个值初始化的临时量并赋值给赋值对象。


4.取整函数

C++常用的取整函数有三个ceil,floor,round
头文件

#include<cmath>

ceil的英文释义:装天花板,装船内格子板;函数功能是向上取整

floor的英文释义:楼层; 地面,地板;函数功能是向下取整

round的英文释义:大约;函数功能是四舍五入

示例:

int _tmain(int argc, _TCHAR* argv[])
{
    float num = 8.6f;
    std::cout << num << " 向上取整为: "<< ceil(num) <<std::endl;
    std::cout << num << " 向下取整为: "<< floor(num) << std::endl;
    std::cout << num << " 四舍五入为: "<< round(num) << std::endl;
    getchar();
    return 0;
}

5.随机数引擎

rand()

基本:使用随机数时,经常见到的是C标准库提供的函数rand(),这个函数会生成一个0到RAND_MAX之间的一个整形数;
分布:为了得到一个给定范围内的随机数,通常会对生成的随机数取余:rand()%n,rand()%(n-m)+m;
种子:通过srand()设置随机数种子,种子不变的情况下,每次程序运行,调用rand(),都会生成相同的随机数序列;
浮点:使用double(rand())/RAND_MAX可以生成0-1范围内的随机浮点数,但精度会有问题;
一般情况下,srand()种子可以使用time(0)进行设置,time()取系统的秒,所以如果srand()和rand()的调用间隔小于1s,则会生成相同的随机数,如:


#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */

    for (size_t i = 0; i < 10; i++)
    {
        srand((unsigned int)time(NULL));
        cout << rand() << endl;
    }

C++11 : default_random_engine
基本:C++11提供了新的随机数生成器,随机数引擎default_random_engine,使用时包含头文件#include;
范围:默认情况下,default_random_engine的生成范围是一个unsigned,可以通过方法min()和max()获取生成范围;
种子:与rand()类似,default_random_engine也需要通过随机数种子改变生成的序列,设置方法可以通过调用方法seed();
分布和浮点:随机数引擎可以通过分布对象设置生成范围,

uniform_int_distribution<unsigned>

uniform_real_distribution<double>

相对rand(),可以使用uniform_real_distribution<>生成随机浮点数,并且不用担心精度问题,随机数引擎的使用方法如下:

#include<random>
    default_random_engine e;//定义随机数引擎
    uniform_int_distribution<unsigned> id(1, 10);//整型分布
    uniform_real_distribution<double> dd(0, 1.0);//浮点型分布

    e.seed(10);//设置随机数种子
    
    for (size_t i = 0; i < 10; i++)
    {
        cout << id(e) << " ; " << dd(e) << endl;
    }

设置random_device rd
这玩意是c++11引入的新的头文件

#include<random>

里面可以实现真随机数的玩意儿
可以给引擎设置这个种子,达到随机数的牛逼plus


std::random_device rd; // Non-determinstic seed source
std::default_random_engine rng3 {rd()}; // Create random number generator

种子值是通过== random_device 类型的函数对象 rd 获得的。每一个 rd() 调用都会返回不同的值,==而且如果我们实现的 random_devic 是非确定性的,程序每次执行连续调用 rd() 都会产生不同的序列。

设置种子
方法

 default_random_engine e;//定义随机数引擎
 e.seed(10);
//创建后再设置种子
 
  default_random_engine n{10};
  default_random_engine e1(32767);
//创建引擎时设置种子

随机种子

default_random_engine e(time(0));
//设置当前时间为引擎种子

random_device rd; // Non-determinstic seed source
default_random_engine rng3 {rd()}; // Create random number generator
//用random_device 产生的真随机数来赋值

6.程序运行计时

#include<windows.h>
 double TIME=0;
LARGE_INTEGER nFreq;
LARGE_INTEGER nBeginTime;
LARGE_INTEGER nEndTime;
QueryPerformanceFrequency(&nFreq);
QueryPerformanceCounter(&nBeginTime);//开始计时  
......//中间运行的程序
QueryPerformanceCounter(&nEndTime);//停止计时
TIME = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;//计算程序执行时间单位为s  
cout << TIME * 1000 <<"ms"<< endl;//乘以1000单位为毫秒



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第一次实践一个多文件的代码 的相关文章

  • 为什么在 C# 中成员初始值设定项中不允许这样做,但在 VB.Net Me 中允许

    我正在将 VB Net 应用程序转换为 C 并注意到在 VB Net 代码中 有一个私有成员变量 它是使用Me像这样 Private m ClassA As New MyCollection Of ClassA Me 当我将其转换为 C 代
  • C++ 模板中的名称查找

    我有一些 C 代码 如果没有 fpermissive 选项 就无法再编译 这是我无法分享的专有代码 但我认为我已经能够提取一个简单的测试用例来演示该问题 这是 g 的输出 template eg cpp In instantiation o
  • 将指针转换为浮点数?

    我有一个unsigned char 通常 这指向一块数据 但在某些情况下 指针就是数据 即 铸造一个int的价值unsigned char 指针 unsigned char intData unsigned char myInteger 反
  • 避免集合已修改错误

    Issue 我有以下代码 foreach var ItemA in GenericListInstanceB ItemA MethodThatCouldRemoveAnyItemInGenericListInstanceB 显然我得到一个错
  • 使用静态类型代替变量

    当您的项目不使用命名空间时 有什么方法可以告诉编译器使用静态类型而不是变量吗 例如 我有一个名为 User 的类 它具有各种静态和非静态方法 假设调用了其中一个静态方法GetUser 我想称之为User GetUser 方法来自一个方法 该
  • C++ 非类型参数包扩展

    我正在编写由单一类型参数化的模板函数 并且具有可变数量的相同类型 而不是不同类型 的参数 它应该检查第一个值是否在其余值中 我想这样写 include
  • 使用 Selenium for C# 登录 Facebook

    我一直在使用 Selenium C 框架并尝试进行 facebook 登录 但没有任何运气 这是我到目前为止得到的 基于这篇文章 使用 Selenium 测试 Facebook Connect 应用程序 https stackoverflo
  • 有没有办法使 C90 标准中的枚举无符号? (符合 MISRA-C 2004 标准)

    我正在尝试找到一种使枚举 无符号 的方法 enum x1 0 x2 x3 uint8 t x2 lt PC LINT MISRA C 2004 will complain about mixing signed and unsigned h
  • C 中“for”循环中的两个变量

    我正在编写一些代码 需要在其中使用两个变量for环形 下面的代码看起来没问题吗 它确实给了我预期的结果 for loop 1 offset loop 2 offset 2 loop 1 gt offset 190 loop 2 lt 190
  • 如何用C++解析复杂的字符串?

    我试图弄清楚如何使用 解析这个字符串sstream 和C 其格式为 string int int 我需要能够将包含 IP 地址的字符串的第一部分分配给 std string 以下是该字符串的示例 std string 127 0 0 1 1
  • Qt mouseReleaseEvent() 未触发?

    我有一个显示图片的库 我们称之为 PictureGLWidget 其中 class PictureGLWidget public QGLWidget 所以 PictureGLWidget 扩展了 QGLWidget 在PictureGlWi
  • 使用互斥锁来阻止临界区外部的执行

    我不确定我的术语是否正确 但这里是 我有一个由多个线程使用的函数来写入数据 在注释中使用伪代码来说明我想要的内容 these are initiated in the constructor int data std atomic
  • Qt:将拖放委托给子级的最佳方式

    我在 QWidget 上使用拖放 我重新实现了 DragEnterEvent dragLeaveEvent dragMoveEvent 和 dropEvent 效果很好 在我的 QWidget 中 我有其他 QWidget 子级 我希望它们
  • 如何解释“错误C2018:未知字符'0x40'?[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 在编译一些代码时 我收到以下信息 错误 C2018 未知字符 0x40 我想知道如何解决这样的问题 这是我要开始的地方
  • 需要使用 openssl 加密和解密文件的示例 C 代码

    我正在用 Linux C 编写代码 我需要使用以下命令来加密和解密文件 openssl 目前 我使用系统命令 des3 e nosalt k 0123456789012345 in inp file out out file 进行加密 使用
  • 在 try catch 块中返回到 catch 内是否不好?这是很好的做法

    在 try catch 块中从 C 中的 catch 块返回值是不好的做法吗 try Some code return 1 catch return 0 哪种使用 try catch 的方法是好的做法 不需要 只要返回的值是你想要的 你可以
  • 如何分析 VSCode 中函数的性能

    我用 C Golang 编写了一个程序 如何找到占用最高 CPU 周期的函数 目的是提高正在执行的程序的性能 2021 年 10 月 金香儿哈娜 https github com hyangah宣布 tweet https twitter
  • 如何使用简历实现一个“一网打尽”的异常处理程序?

    我想知道我怎样才能写一个抓住他们全部应用程序级别的异常处理程序将为用户提供恢复应用程序流程的选项 如果您正在运行 Windows 窗体应用程序 将处理程序添加到Application ThreadException event
  • double 类型的静态类成员的常量表达式初始值设定项

    在 C 11 和 C 14 中 为什么我需要constexpr在下面的代码片段中 class Foo static constexpr double X 0 75 而这会产生编译器错误 class Foo static const doub
  • 具有多种类型的 C# 泛型类型推断

    我有以下通用方法 用于将一种类型的输入对象序列化为超类型 如下所示 public string SerialiseAs

随机推荐

  • kvm故障-虚拟机通过镜像创建虚后无法加载eth0网卡,显示网卡为ens3

    虚拟机通过镜像创建虚后无法加载eth0网卡 显示网卡为ens3 一 首先创建eth0网卡文件配置好 cd etc sysconfig network scripts TYPE Ethernet PROXY METHOD none BROWS
  • [MATLAB]使用for循环来寻求斐波那契(Fibonacci)数列中第一个大于10000的元素

    问题描述 斐波那契数列是这样一个数列 1 1 2 3 5 8 13 21 第n个数是第 n 1 个数和第 n 2 个数的和 f 1 1 for k 1 100 f k 2 f k f k 1 if f k gt 10000 break en
  • 【Boost C++ 库】托管共享内存详解

    文章目录 1 托管共享内存 Managed Shared Memory 1 1 使用托管共享内存 1 2 在托管共享内存中创建数组 1 3 删除共享内存中的对象 1 4 对托管共享内存的原子访问 传送门 gt gt AutoSAR入门和实战
  • (二)Qt下多线程实现多个海康工业相机内触发采集回调取流显示

    系列文章目录 提示 这里是该系列文章的所有文章的目录 第一章 一 Qt下实现多个海康工业相机内触发采集回调取流显示 第二章 二 Qt下多线程实现多个海康工业相机内触发采集回调取流显示 文章目录 系列文章目录 前言 一 创建线程类 二 关联线
  • 【备忘录】英译汉接口

    import requests import random import json from hashlib import md5 Set your own appid appkey appid 2xxx appkey kxxx For l
  • 数字电路-二进制转BCD码

    BCD码实际上就是将原本的十进制数的每一位用一个4位二进制数表示 每一位0 9 二进制4位能够表达的数字范围是0 15 由此可见BCD码的一段与普通四位二进制来表示十进制位有6的进制差 所以这就是二进制转化为BCD码的关键所在 下面来讲讲主
  • TP5 一对多关联查询

    A表 car 购物车 goodid 商品ID sizeid 规格ID B表 commondity 商品表 id 对应goodid 商品名称 title C表 size 规格表 id对应sizeid 规格名称 text 在购物车页面 我们需要
  • CMake 编译时出现错误 coffe转换到 COFF 期间失败: 文件无效或损坏

    转 https blog csdn net feiyang2010jin article details 46576463 C Windows Microsoft NET Framework v4 0 30319 D Program Fil
  • 如何修复d3dcompiler_47.dll缺失?多种解决方法分享

    在使用Windows操作系统的过程中 有时候会遇到d3dcompiler 47 dll缺失的情况 这个问题可能会导致某些应用程序无法正常运行 因此需要及时解决 本文将介绍如何修复d3dcompiler 47 dll缺失的问题 一 什么是d3
  • 服务器怎么用u盘传文件进去_本机向windows服务器传输文件的三种方法

    闲来无事 在腾讯云上申请了一个免费的服务器 想将自己写的网页发布到服务器上 服务器的申请很简单 百度搜索 腾讯云 然后新人第一次注册能申请到免费一个月的云主机 虽然配置不怎么高 但是还是能用的 这是我申请到一个云主机 那么如何将我们本机的文
  • 关于在宸曜科技7160gc上的ubuntu16.04系统下计算资源浪费问题

    1 如果使用核显来显示会占用cpu的资源 即使此时hdmi的线插在显卡上 此时会影响使用cpu来执行程序的效率 最直接体现的是程序的数据发送频率会降低很多 2 如何确定是否是cpu显示还是显卡显示了 需要通过system setting里面
  • 南科大于仕琪团队招聘研究助理教授,博士后

    南方科技大学于仕琪副教授团队招聘研究助理教授 博士后 博士生和硕士生 团队特色 专注步态识别方向十余年 在步态识别方向具有较高的影响力 研究助理教授 研究方向 步态识别 行人视频合成 图像和视频中的人体建模 视频中人的行为分析及相关 要求
  • 分布式系统架构——CAP理论

    1 什么是CAP 在分布式系统中 任何存储系统 有状态服务 都会涉及到CAP定理 Consistency 一致性 简称C 在同一时刻所有节点是具有同样的数据副本 每个节点的数据要保证实时同步 Availability 可用性 简称A 对于一
  • Ubuntu14.04 :By not providing "FindEigen3.cmake" in CMAKE_MODULE_PATH

    Ubuntu14 04 By not providing FindEigen3 cmake in CMAKE MODULE PATH 今天下载了hector slam的源码想要跑一下 结果编译不能通过 并且一直出现如下错误 烦得一逼 研究了
  • 兼容性测试真实案例-餐饮APP

    兼容性测试真实案例 某餐饮APP 项目背景 APP是以电商加直播为载体 供应链为核心 辅以短视频和直播内容分销带货 以及积分商城体系 本项目主要对APP的商品列表及商品采购流程及涉及页面 针对不同品牌 操作系统 分辨率的机型 验证APP是否
  • jenkins和jdk安装教程(安装支持jdk8的最新版本)

    1 安装版本查看 Jenkins稳定版 2 进入清华镜像 下载对应版本的rpm包 清华镜像 3 已rpm安装过Jenkins 现在先卸载 1 卸载 rpm e jenkins 2 检查是否卸载成功 rpm ql jenkins 3 彻底删除
  • (大集合)AI工具和用法汇总—集合的集合

    AI 工具和用法汇总 汇集整理 by Staok 瞰百 源于相关资料在我这慢慢越积累越多 到了不得不梳理的程度 文中有许多内容作者还没有亲自尝试 所以很多内容只是罗列 但信息大源都已给出 授人以渔 欢迎 PR 补充细节内容 比如 好的教程
  • HTML+CSS简单复习

    目录 什么是HTML CSS VSCode编辑器 HTML基本结构和属性 HTML初始代码 HTML注释 标题与段落 文本修饰标签 图片标签 路径的引入 链接标签 锚点 特殊字符 列表标签 表格标签 表单标签 div和span CSS基础语
  • 【机器学习笔记4】逻辑回归模型

    目录 什么是逻辑回归 Sigmoid函数 决策边界 逻辑回归的损失函数 为什么平方误差模型不可行 对数损失函数 单个样例损失 整体损失函数 梯度下降算法 补充 F1 score评价指标 F1 Score简介 相关概念 F Score 示例及
  • 第一次实践一个多文件的代码

    收获 1 ifndef防止头文件重复包含 为了避免同一个头文件被包含 include 多次 C C 中有两种宏实现方式 一种是 ifndef方式 另一种是 pragma once方式 ifndef 标识符A 每一个头文件都要有自己独特的标识