中文编程命名法(第2版)

第1版:https://blog.csdn.net/acebdo/article/details/20643503

第1版写于4年前,在这期间我也写了很多代码,在实际应用中发现很多不足,所以对第1版进行修改和扩充。

偷懒原则1

有时候为了偷工减料,可以使用一些比较短的名字。

double f算圆面积(double r) {//半径r
    return r * r * pi;//圆周率pi
}
void f什么也没发生() {
    for (int i = 0; i != 10; ++i);//计数i
}

偷懒的名称必须清晰易懂,看到变量名就容易想到具体用途,尽量用在类/结构/函数内部。如果容易忘记变量用途,那就写完整名称,或者加注释,加注释的方法仅限函数局部变量和函数参数。

一些常见的可以偷懒的命名:

  • 用于循环的i、j、k
  • 表示限制/数量的n
  • 异常处理中的e

一些约定俗成的名称也遵循偷懒原则:

  • python中表示对象自身的self

自定义类型

全部用大写的英文单词首字母作为前缀:类C(class),结构S(struct),接口I(interface),联合U(union),窗口W(window)。

struct S结构 {};
class C类 {};
union U联合 {};

异常类X(打叉)

class X未实现 : public std::runtime_error {
public:
    X未实现(): std::runtime_error("该函数未实现") {}
};

枚举

枚举类型用E(enum),枚举值用e(enum)。

enum E状态 {
    e好,
    e坏,
    e不好不坏
};

函数

函数用f(function)

void f计算() {}

声明函数时,函数形参不用加前缀。因为这里的参数名就跟注释一样的仅供参考。

double f算圆面积(double 半径);

对于函数类和创建函数对象的函数,用大写的F

template<typename t>
struct F小于 {
    operator()(const t &a0, const t &a1) const {
        return a0 < a1;
    }
};
std::function<int()> F返回整数(int a) {
    return [=]()->int {
        return a;
    };
}
const std::function<int()> f返回1 = F返回整数(1);

函数的二级前缀

根据函数功能的不同,使用不同的二级前缀,只用一个字符:

创建c(create/construct)

class C工厂 {
    std::shared_ptr<C人> fc人();//造人
}

因为c是创建(create)和构造(construct)的缩写,所以c也可以表示构造自身。为了区分是创建其他对象还是自己创建自己,我对类和结构的“fc”进行限制:类中的fc用于创建其他对象,结构的fc用于创建自己。

例如:二维向量结构

struct S向量2 {//二维向量
    double x = 0, y = 0;
    S向量2() = default;
    S向量2(double X, double Y);//直接给x,y赋值
    static S向量2 fc大小方向(double 大小, double 方向);//根据大小方向计算x,y
}

获取g(get)/设置s(set)

double fg大小();
double fg方向();
void fs大小(double);
void fs方向(double);

判断是/否的i(is)

bool fi创建() const;
bool fi有效() const;

转换到某个类型用t(to)

struct S向量2 {
    double x = 0, y = 0;
    S向量3 ft向量3(double z = 0) {
        return {x, y, z};
    }
}

枚举e(enumerate),表示该函数支持简单迭代。

def fe零到几(a: int):
    for i in range(a):
        yield i
class C人群 {
    private List<C人> ma人;
    public IEnumerable<C人> fe活人() {
        foreach (C人 v人 in ma人) {
            if (v人.fi存活()) {
                yield return v人;
            }
        }
    }
}

函数的二级前缀只能用动词,不能用名词,用名词容易跟动词混淆。

变量/常量

变量v(variable),全局变量g(global),函数参数a(argument),类/结构成员变量m(member),编译期常量c(constant),窗口控件w(window/widget)
如果不知道一个变量该用什么前缀,就用v吧。

示例:人类

class C人 {
public:
    std::string m姓名;
    int m年龄;
}

示例:函数

constexpr double c圆周率 = 3.1415926;
double f加圆周率(double a) {
    const double v = a + c圆周率;
    return v;
}

示例:全局变量+单例类

int g大家都可以访问 = 0;
class C单例 {
    static C单例 *g这;
    C单例() {
        assert(g这 == nullptr);
        g这 = this;
    }
};
C单例 *C单例::g这 = nullptr;

我觉得没必要用s(static)来表示静态变量,因为静态变量还要按访问权限继续细分,不然只有个s容易混淆(虽然静态函数和成员函数都用f也很容易混淆),而且静态变量的使用频率没其他变量那么频繁。所以我对公有静态变量用g,私有静态变量用m,局部静态变量用v。

设计用户界面给控件命名可以用w,HTML的元素id也可以用w。

w的定义比较模糊,容易和其他一级前缀和变量的二级前缀混淆

<input id="w姓名" class="W文本框" type="text"/>
<input id="w年龄" class="W文本框" type="text"/>
<textarea id="w备注" class="W文本框" rows="5"></textarea>

变量的二级前缀

根据变量的功能/类型可以再加二级前缀。使用类型作为变量的二级前缀时,必须是广泛使用的类型,比如指针p、数组a、函数f。

是否i,和前面一样

void f强行成功() {
    mi成功 = true;
}
bool fi成功() {
    return mi成功;
}

指针p。句柄也用p,因为句柄是特殊的指针。

int *vp整数 = new int();
std::shared_ptr<int> vp还是整数 = std::make_shared<int>();
HWND vp窗口 = nullptr;

函数指针f,c#的委托也可以用这个

std::function<void(std::function<void()>)> vf = [](std::function<void()> af) {  
    af();  
}  

数组a(array)。像其他包含多个变量的容器也用a作为前缀,例如:列表、元祖、字典、集合。

std::vector<S学生> va学生;

不要学匈牙利命名法i、n、w、sz、str、h这种前缀。

类型别名

类型别名用t,同时可以根据具体类型添加二级前缀

typedef S向量2 t向量2;
typedef int *tp整数;
typedef ComPtr<ID3D12Device> tp设备;
typedef std::shared_ptr<I工厂> tp工厂;
typedef std::function<void()> tf;

模板参数也用t

template<typename t> void f(const t &) {
}

可变参数模板中懒得想名字可直接取名“参数”

template<typename...t参数>
void f(t参数 &&...a参数);

重名的处理方法(序号后缀)

虽然前面写了一堆前缀,但实际应用中还是会出现重名。

比如:出现了功能和前面一样的临时变量

bool f判断(C判断 a判断) {
    C判断 v判断 = a判断;
    {//为了满足文章需求强行定义临时变量
        C判断 v判断;//重复了
    }
    return v判断.m判断;
}

上面有2个“v判断”,虽然编译不会出错,但是局部作用域需要用到第1个“v判断”呢?这时候在变量名后面加数字区分,从0开始。

C判断 v判断0 = a判断;
{
    C判断 v判断1 = v判断0;
}

在循环中也可以加数字区分

for (int i0 = 0; i0 != 10; ++i0) {
    for (int i1 = 0; i1 != 10; ++i1) {
    }
}

序号后缀法用于函数时,仅限在类内部使用,因为公开的函数给别人用的话容易搞不清楚函数0123的具体作用以及他们的区别。如果真的重复了想不出别的名字了,见后面的命名分类。

偷懒原则2

偷懒原则2:根据类型名就可以知道用途,且没有其他变量/参数时,可以只写前缀,省略中文名。

void f(C干一件大事 a) {//不用写中文也知道a是用来干一件大事的
    C干一件大事 v;//不用写中文也知道v是用来干一件大事的
}

往排序函数扔比较函数时,比较函数中的参数名也省略

std::sort(va人.begin(), va人.end(), [](const C人 &a0, const C人 &a1) {
    return a0.m年龄 < a1.m年龄;
});

偷懒原则1与偷懒原则2不能同时使用,因为容易造成歧义。

偷懒原则2只限局部变量/参数使用

避免意思重复的名称

就像“凯旋归来”这个充满争议的病句一样,不要存在意思重复的命名。

比如“va数组变量”这个就反了意思重复的错误,v表示变量,a表示数组,后面再加个“数组变量”就重复了。就算不知道取什么名字也不能取意思重复的词,命名成“va东西”也行,或者根据偷懒原则2直接命名“va”。

再比如异常类的前缀X,既然X代表了异常,就不要在名称中加“异常”、“错误”什么的。

1.5级前缀:命名分类

有时候定义的变量名、函数名太多了,有时候类中对外开放的函数和内部使用的函数名称一样,这时候使用命名分类区分不同函数的功能/作用范围

命名分类直接写“中文名_”

class I加减 {
    virtual int f接口_加() = 0;
    virturl int f接口_减() = 0;
};
class C加 : public I加 {
public:
    int f接口_加() override;
    int f接口_减() override;
private:
    int f内部_加0();
    int f内部_加1();
    int f内部_减();
}

版本后缀

版本后缀用来区分功能相似但版本不同的函数。

版本后缀有2种写法:第一种直接加英文字母,第二种加下划线再写中文的“_版本名”

例如:“S向量2::fc大小方向”按平面角单位分度、弧度版本

static S向量2 fc大小方向r(double 大小, double 弧度);
static S向量2 fc大小方向d(double 大小, double 度);

例如:三维坐标系中根据手性分左手坐标系和右手坐标系,根据左右手不同在函数后面加l或r。

不加一级前缀的东西

命名空间:大家不加,我也不加。

模块/包:理由同上。

宏:比较少用,所以不加。

(该文章会根据实际情况不断修改,最后更新时间:2018年8月2日)

猜你喜欢

转载自blog.csdn.net/acebdo/article/details/80554791