2023-4-4-C++应该怎么设计一个好的项目结构

简介: 2023-4-4-C++应该怎么设计一个好的项目结构

😉一、单个文件如何设计结构

作为一名程序员,要时刻记住一句话,那就是“程序是写给程序员的”。这个程序员可以是测试,可以是基于你写的代码继续开发的同事,更可能是你未来的自己。如果你的项目文件之间引用混乱,文件与文件之间的逻辑“错综复杂”到最后一定会造成可想而知的几个结果:

  • 测试人员让你改BUG的时候你花了大量的时间回顾了自己之前写的代码。
  • 你有事想请假的时候领导让别人完成你未完成的开发任务,结果你的同事看不懂你的代码,疯狂的call你。
  • 你进行应用升级的时候或应用迭代的时候,你为了兼容之前错乱的写法继续采取了错误的写法,然后写出了更错乱的代码,然后长此以往陷入了恶性循环。

如果要尽量避免出现上述问题,就要在日常的编码中时刻注意自己的规范,将代码写的规范些会给自己避免很多问题,将一位大佬对我曾说过的话跟大家分享“要把代码放在它应该放到的位置”。

区别对待.h和.cxx

C++是面向对象的一门语言,它具有类的概念,在你新建一个类的时候,大多数编译器会自动地给你生成名称相同的两个文件,一个.h文件,一个.cpp文件,也就是我们常说的头文件和源文件。

在编程过程中建议程序员一定要区别对待这两种文件,只在头文件里放置声明,包括类的声明,函数的声明,变量的声明,而它们的实现就全部放置到源文件里面。这是一种编码风格,是一种程序员之间心照不宣的写法,这样的编程风格有助于代码条理的清晰,让阅读者读起来比较方便。“声明”向计算机介绍名字,它说,“这个名字是什么意思”。而“定义”为这个名字分配存储空间。无论涉及到变量时还是函数时含义都一样。无论在哪种情况下,编译器都在“定义”处分配存储空间。对于变量,编译器确定这个变量占多少存储单元,并在内存中产生存放它们的空间。对于函数,编译器产生代码,并为之分配存储空间。函数的存储空间中有一个由使用不带参数表或带地址操作符的函数名产生的指针。定义也可以是声明。如果该编译器还没有看到过名字A,程序员定义int A,则编译器马上为这个名字分配存储地址。

声明常常使用extern关键字。如果我们只是声明变量而不是定义它,则要求使用extern。对于函数声明, extern是可选的,不带函数体的函数名连同参数表或返回值,自动地作为一个声明。

在完成类的声明包括其中成员函数和成员变量的定义之后,一定要在头文件中加入条件编译语句

😆小知识

为什么要加条件编译语句:加条件指示符的最主要目的是防止头文件的重复包含和编译

在c语言中,对同一个变量或者函数进行多次声明是不会报错的。所以如果h文件里只是进行了声明工作,即使不使用# ifndef宏定义,多个c文件包含同一个h文件也不会报错。

但是在c++语言中,#ifdef的作用域只是在单个文件中。所以如果h文件里定义了全局变量,即使采用#ifdef宏定义,多个c文件包含同一个h文件还是会出现全局变量重定义的错误。而#ifndef作用域是全局的,使用#ifndef可以避免上面所说的全局变量重定义的错误

#ifndef x                 //先测试x是否被宏定义过
#define x
   程序段1    //如果x没有被宏定义过,定义x,并编译程序段 1
#endif   
  程序段2   //如果x已经定义过了则编译程序段2的语句,“忽视”程序段 1

😆小知识

C++项目的编译原理:在编译的时候编译器编译的是项目的cpp文件,头文件是不能被编译的。“#include”叫做编译预处理指令,可以简单理解成,在cpp中的#include指令把对应h中的代码在编译前添加到了cpp中代理掉了Include这条语句。每个cpp文件都会被编译,生成多个obj文件,然后所有的obj文件链接起来就是最终的可执行程序。

为当前文件定义namespace命名空间,建议根据文件路径以及已有命名空间进行镶嵌定义,注意不要使用std这种已经命名空间,否则会留下很多隐患

😆小知识

为什么定义命名空间

所谓命名空间,是一种将程序库名称封装起来的方法,它就像在各个程序库中立起一道道围墙,或者说指定了很多同名的孩子(变量)的归属,因为随着C++的不断发展,关键字和变量越来越多,在工程中使用有遇到重命名的问题越来越多,所以引入了命名空间,让每一个即使是同名的变量都可以辨识。

命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。

😲冷知识

有时我们并不希望命名空间被局部的环境之外知道,此时名字似乎多余了,因此我们可以省去这个名字 直接改写为:

namespace

{

int a;

}

定义无名命名空间后,外部即不能得知无名命名空间的成员名字,即不让外部知道我的成员名字及其调用

由于没有名字,所以其它文件无法引用,它只能在本文件的作用域内有效,

它的作用域:重无名命名空间声明开始到本文件结束。在本文件使用无名命名空间成员时不必用命名空间限定。其实无名命名空间和static是同样的道理,都是只在本文件内有效,无法被其它文件引用。

如果你的代码中应用到了其他的命名空间的变量或者方法,这时候需要using一下

我不建议在头文件中使用 using namespace xxx 语句,不仅是对std空间。如果头文件a.h使用了该语句,那么所有与a.h有关系的文件都会默认使用该语句,也就是说引用a.h头文件的都被迫using了,所以引用头文件的越多就会越容易导致命名冲突。所以当我们在写成熟的代码的时候,需要用库里的什么就打开什么。这就有效的防止了命名冲突。

对于使用自己定义的其他命名空间,建议学习上文中的这种写法,用using=…的方式能够缩小命名空间的存在范围,可以进一步避免命名冲突的问题出现。

😆小知识

什么是std:std::是个名称空间标识符,C++标准库中的函数或者对象都是在命名空间std中定义的,所以我们要使用标准库中的函数或者对象都要用std来限定。

以上就是目前我在C++开发中对于单个文件的结构设计,如果有更好的建议希望读者能够在评论区中分享。


🐱‍🐉二、复杂的工程如何设计结构

对于如何设计一个好的项目结构本身是一件仁者见仁智者见智的事,如果是自己的项目自己玩那怎样设计无所谓,自己开心就好,文件放在什么目录下,目录的名字叫什么都可以,但是如果是一个很多人都会参与的工程,很多人都会使用的工程,那么就必须要有一个清晰的工程结构方便大家,泪目了。

以下是我在整理出来的项目于结构,也是参考了很多大型开源项目的工程结构又进行了调整,最终的结构如下图所示,该工程是一个Cmake工程所以会有一个CmakeLists.txt。

该目录结构如上图所示

cmake-build-debug-visual-studio=该项目用VS编译后的可执行文件所在

dist=可部署项目,其中包括各种环境依赖的安装包,包括工程跑起来所需要的exe和dll等

doc=该工程的一些说明文件或者有关该工程的一些说明文档

examples=该工程的一些示例工程,简单的demo

include=该工程的头文件所在的目录

lib=该工程依赖的一些开源库的dll所在的目录

res=该工程以来的一些图片资源或者其他资源

src=该工程的源文件所在的目录

test=该工程的测试工程所在的目录

thirdparty=该工程依赖的一些开源库的头文件或者源文件所在目录

tools=该工程提供的工具包,可以是一些非常好用的工具


🧊文章总结

提示:这里是文章总结:

  本文讲了关于C++如何设计单文件的结构以及如何设计项目结构,希望小伙伴有所收获,一个好的项目结构对开发起着事半功倍的作用,希望读完这篇文章的伙伴们可以分享一下对文章的意见和建议,可以通过评论或者私信的方式与我交流哈



目录
相关文章
|
6月前
|
C++
C++选择结构
C++选择结构
84 0
WK
|
9天前
|
机器学习/深度学习 人工智能 算法
那C++适合开发哪些项目
C++ 是一种功能强大、应用广泛的编程语言,适合开发多种类型的项目。它在游戏开发、操作系统、嵌入式系统、科学计算、金融、图形图像处理、数据库管理、网络通信、人工智能、虚拟现实、航空航天等领域都有广泛应用。C++ 以其高性能、内存管理和跨平台兼容性等优势,成为众多开发者的选择。
WK
28 1
|
27天前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
223 3
|
6月前
|
C++
C++程序中的选择结构
C++程序中的选择结构
43 2
|
2月前
|
C++
【C++案例】一个项目掌握C++基础-通讯录管理系统
这篇文章通过一个通讯录管理系统的C++项目案例,详细介绍了如何使用C++实现添加、显示、删除、查找、修改和清空联系人等功能。
39 3
|
2月前
|
C++
【C++基础】程序流程结构详解
这篇文章详细介绍了C++中程序流程的三种基本结构:顺序结构、选择结构和循环结构,包括if语句、三目运算符、switch语句、while循环、do…while循环、for循环以及跳转语句break、continue和goto的使用和示例。
42 2
|
6月前
|
算法 测试技术 C++
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
|
6月前
|
C++ 容器
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
|
3月前
|
C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
32 5
|
3月前
|
C++
c++学习笔记03 程序流程结构
C++学习笔记,主要介绍了程序流程结构,包括顺序结构、选择结构和循环结构。选择结构中详细解释了if语句、三目运算符和switch语句的用法和注意事项。循环结构部分则涵盖了while循环、do-while循环和for循环的语法和使用技巧。此外,还介绍了跳转语句,包括break、continue和goto语句的用途和用法。
35 0