RoadMap7:C++构建代码库(命名空间与头文件)

摘要:在编写一个项目工程时,显然你大可把所有的代码都写在一个main函数里面,但是这对后续的修改和维护、代码功能的理解会造成很大的不便。构建代码库(Structuring Codebase)是围绕着软件工程处理 如何清晰地实现代码组织如何实现功能模块化 需求提出的处理方法。围绕着这个需求,本章内容将着重讲解如何通过 命名空间头文件 的方法进行代码组织。

1. 命名空间(NameSpaces)

1.1 作用域(Scope)

命名空间本质上就是一种特殊的作用域。因此,在了解命名空间前必须要知道什么是作用域。作用域这个概念在介绍变量的生命周期的时候已经引入,相信对作用域都有模糊的概念,但是并没有做详细的定义。

作用域:指C++程序中变量、函数、类和其他标识符的可见性和可访问性。它决定了这些标识符的寿命和范围。 通常包括:局部作用域、全局作用域、命名空间和类作用域。

局部作用域:被定义在 函数 或者 代码块{ } 内部的变量具有局部作用域。它们无法被外界访问。当然他们的生命周期也仅限于这个函数内部或者带模块内。

全局作用域:相对地,被定义在 函数 或者 代码块{ } 外部的变量具有全局作用域。它们可以在任意位置被访问。当然他们的生命周期也伴随着整个程序的执行。

#include<iostream>

// 全局变量
int globalVar = 10;

void localExapmle(){
    int localVar = 99;
    std::cout << "local Var: " << localVar << std::endl;    // 输出:local Var: 99
    std::cout << "global Var: " << globalVar << std::endl;  // 输出:global Var: 10
}


int main(){
    localExapmle();
    // std::cout << "local Var: " << localVar << std::endl; 会报错,显示变量未定义
    std::cout << "global Var: " << globalVar << std::endl;    // 输出:global Var: 10
}

1.2 命名空间

命名空间本质上就是一种介于全局作用域和局部作用域的特殊的作用域。被使用 命名空间关键字 namespace:

#include<iostream>
// 命名空间的定义
namespace NameofNamespace{
	int namespaceVar = 5;
}

int main(){
	// 通过命名空间操作符 :: 访问命名空间内部变量
	std::cout << NameofNamespace::namespaceVar << std::endl;   //输出:5
}

定义在 代码块{ } 内部的变量 (e.g. namespaceVar) 具有命名空间作用域。它们无法被外界访问,但是可通过命名空间操作符::进行访问。当然他们的生命周期也伴随着整个程序的执行。

细心的同学已经发现 std::cout 本质上也是一种命名空间,在很多代码中以下代码段:

#include<iostream>
using namespace std;
namespace NameofNamespace{
	int namespaceVar = 5;
}
int main(){
	// 通过 using namespace std 指定整个文件的命名空间都默认为 std,因此使用cout和endl都不需要在前面加入命名空间;
	cout << NameofNamespace::namespaceVar << endl;   //输出:5
}

通过 using namespace std 指定整个文件的命名空间都默认为 std,因此使用cout和endl都不需要在前面加入命名空间;当然,也可使用关键字 using namespace 指定整个文件的命名空间都默认为自定义的命名空间,如:NameofNamespace 或者同时指定多个命名空间。

#include<iostream>
// 自定义命名空间
namespace NameofNamespace{
	int namespaceVar = 5;
}

using namespace NameofNamespace; // 使用命名空间需要在自定义命名空间之后
using namespace std;  // 同时定义多个命名空间

int main(){
	// 已经指定整个文件的命名空间,因此使用namespaceVar,cout和endl都不需要在前面加入命名空间;
	cout << namespaceVar << endl;   //输出:5
}

当然,命名空间也支持嵌套,通过 外层命名空间::内层命名空间::变量名 来访问,如:

#include<iostream>
using namespace std;  // 同时定义多个命名空间

// 自定义命名空间
namespace Outer{        //外层命名空间
    namespace Inner{    //内层命名空间
        int innerVar = 66;
    }
	int outerVar = 5;
}

int main(){
	cout << "outerVar: " << Outer::outerVar << endl;   //输出:outerVar: 5
    cout << "innerVar: " << Outer::Inner::innerVar << endl;   //输出:innerVar: 66
}

1.3 类作用域

上一节我们讲解了通过命名空间操作符 :: 可以访问命名空间内部变量,但应该不少人在类的使用时也见过其他类似的操作符,如 ->. ,他们和命名空间操作符 :: 有什么关系或者差异呢?

操作符 :: 可以结合类名称用于访问类中定义的的静态变量或者用于定义类中已经声明的函数,该运算符把类名视为一种特殊的命名空间

操作符 . 用于访问对象的成员,相比较于操作符 :: ,操作符 . 的操作内容是类实例化的对象(如例子中的obj,而非类MyClass);

操作符 -> 用于访问对象的成员,相比较于操作符 :: ,操作符 -> 的操作内容是类实例化的指针对象(如例子中的obj1,而非类MyClass);

#include <iostream>

class MyClass {
public:
    static int staticMember;
    int nonStaticMember;
    void printer();
    MyClass(int value) : nonStaticMember(value) {}
};

int MyClass::staticMember = 7;
// int MyClass::nonStaticMember = 7;  报错,非静态变量不能在类的外部定义;
void MyClass::printer(){std::cout << "I am function! " << std::endl;};

int main() {
    MyClass obj(10);
    MyClass* obj1;
    std::cout << "Static member with ::: " << MyClass::staticMember << std::endl;
    std::cout << "Static member with .: " << obj.staticMember << std::endl;
    std::cout << "Static member with ->: " << obj1->staticMember << std::endl;
    std::cout << "Non-static member: " << obj.nonStaticMember << std::endl;
}

// Output:
// Static member with namespace: 7
// Static member with .: 7
// Static member with ->: 7
// Non-static member: 10

2. 头文件

头文件,或者说头文件-源文件的代码构建方式是另外一种常见的优化代码结构方法。有利于提高功能模块化、代码可读性和提高维护效率。

  1. 头文件(.h或.hpp)

头文件通常扩展名为.h或.hpp,负责声明多个源文件所需的类、函数和变量。它们充当代码不同部分之间的接口,使管理依赖关系和快速了解代码功能变得更容易,并减少重复代码。

// example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H

class Example {
public:
    void printMessage();
};

#endif
  1. 源文件(.cpp)

扩展名为.cpp的源文件负责实现相应头文件中定义的实际功能。它们包括所需的头文件,并提供函数和类方法定义。

// example.cpp
#include "example.h"
#include <iostream>

void Example::printMessage() {
    std::cout << "Hello, code splitting!" << std::endl;
}
  1. 分离编译

显然,把功能函数/类写在独立的cpp中,与main函数分离,功能模块化效果大大提升,也更容易理解代码的功能。而分离编译可以有效地提高代码的维护效率。

因为main函数所在的文件和功能cpp文件独立存在,当修改功能时仅仅需要修改cpp文件。因此分离编译的功能就是把main.cpp和功能cpp文件独立编译为不同的对象文件(.o文件),然后再通过连接对象文件,构建最后的可执行文件(.exe文件)。这意味着当只修改某个功能cpp文件时,只需要重新生成它所对应的对象文件(.o文件),而其他文件不需要重新编译,这将大幅度提升编译效率。

# Compile each source file into an object file
g++ -c main.cpp -o main.o
g++ -c example.cpp -o example.o

# Link object files together to create the executable
g++ main.o example.o -o my_program

3. 总结

构建代码库(Structuring Codebase)是围绕着软件工程处理 如何清晰地实现代码组织如何实现功能模块化 需求提出的处理方法。围绕着这个需求,本章内容将着重讲解如何通过 命名空间头文件 的方法进行代码组织。

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
开发代码流程规范(roadmap)是一个指导开发人员在软件开发过程中按照一定的规范和流程进行工作的指南。下面是一个300字的回答: 制定开发代码流程规范(roadmap)的目的在于提高开发效率、软件质量和代码可维护性。它是一个明确的路线图,帮助开发团队有效地组织开发工作,并确保项目按时交付。 首先,开发代码流程规范应包括项目开发的各个阶段。这包括需求分析、设计、编码、测试和发布部署。规范应指明每个阶段的任务和要求,并为每个阶段分配合适的时间和资源。 其次,规范中应明确定义代码质量标准和规范。这包括代码注释、命名规范、代码缩进、代码复用等方面的规定。还应规定代码提交和审核的流程,确保每个提交都经过团队成员的评审和测试。 另外,规范应指导开发人员在使用版本控制系统时的操作流程。版本控制系统(如Git)可以追踪代码的修改历史,帮助团队成员协同工作。规范应规定分支管理策略、提交信息的格式和代码合并的流程,以确保团队成员之间的协作顺畅。 最后,规范应包括持续集成和持续交付的流程。持续集成是将开发人员的代码频繁地合并到主干分支,并自动运行测试来验证代码的正确性。持续交付将经过测试和审核的代码部署到生产环境中,以便用户可用。规范应规定持续集成和持续交付的频率和流程,确保代码的及时交付和发布。 总之,开发代码流程规范(roadmap)是一个组织和指导开发工作的重要工具。它确保团队按照一定的规范和流程进行工作,提高开发效率和软件质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值