[翻译] Effective C++, 3rd Edition, Item 3: 只要可能就用 const

Zemax操作33--5倍激光扩束器 5倍扩束 阅读详情

Item 3: 只要可能就用 const

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.youkuaiyun.com/fatalerror99/

关于 const 的一件美妙的事情是它允许你指定一种 semantic(语义上的)约束:一个特定的 object(对象)不应该被修改。而 compilers(编译器)将执行这一约束。它允许你通知 compilers(编译器)和其他程序员,某个值应该保持不变。如果确实如此,你就应该明确地表示出来,因为这样一来,你就可以谋求 compilers(编译器)的帮助,确保这个值不会被改变。

keyword(关键字)const 非常多才多艺。在 classes(类)的外部,你可以将它用于 global(全局)或 namespace(命名空间)范围的 constants(常量)(参见 Item 2),以及那些在 file(文件)、function(函数)或 block(模块)scope(范围)内被声明为 static(静态)的对象。在 classes(类)的内部,你可以将它用于 static(静态)和 non-static(非静态)data members(数据成员)上。对于 pointers(指针),你可以指定这个 pointer(指针)本身是 const,或者它所指向的数据是 const,或者两者都是,或者都不是:

char greeting[] = "Hello";

char *p = greeting;                    // non-const pointer,
                                       // non-const data

const char *p = greeting;              // non-const pointer,
                                       // const data

char * const p = greeting;             // const pointer,
                                       // non-const data

const char * const p = greeting;       // const pointer,
                                       // const data

这样的语法本身其实并不像表面上那样反复无常。如果 const 出现在星号左边,则指针 pointed to(指向)的内容为 constant(常量);如果 const 出现在星号右边,则 pointer itself(指针自身)为 constant(常量);如果 const 出现在星号两边,则两者都为 constant(常量)。

当指针指向的内容为 constant(常量)时,一些人将 const 放在类型之前,另一些人将它放在类型之后星号之前。两者在意义上并没有区别,所以,如下两个函数具有相同的 parameter type(参数类型):

void f1(const Widget *pw);         // f1 takes a pointer to a
                                   // constant Widget object

void f2(Widget const *pw);         // so does f2

因为它们都存在于实际的代码中,你应该习惯于这两种形式。

STL iterators(迭代器)以 pointers(指针)为原型,所以一个 iterator 在行为上非常类似于一个 T* pointer(指针)。声明一个 iterator 为 const 就类似于声明一个 pointer(指针)为 const(也就是说,声明一个 T* const pointer(指针)):不能将这个 iterator 指向另外一件不同的东西,但是它所指向的东西本身可以变化。如果你要一个 iterator 指向一个不能变化的东西(也就是一个 const T* pointer(指针)的 STL 对等物),你需要一个 const_iterator:

std::vector<int> vec;
...
const std::vector<int>::iterator iter =     // iter acts like a T* const
  vec.begin();
*iter = 10;                                 // OK, changes what iter points to
++iter;                                     // error! iter is const

std::vector<int>::const_iterator cIter =    // cIter acts like a const T*
  vec.begin();
*cIter = 10;                                // error! *cIter is const
++cIter;                                    // fine, changes cIter

对 const 最强有力的用法来自于它在 function declarations(函数声明)中的应用。在一个 function declaration(函数声明)中,const 既可以用在函数的 return value(返回值)上,也可以用在个别的 parameters(参数)上,对于 member functions(成员函数),还可以用于整个函数。

一个函数返回一个 constant value(常量值),常常可以在不放弃安全和效率的前提下尽可能减少客户的错误造成的影响。例如,考虑在 Item 24 中考察的 rational numbers(有理数)的 operator* 函数的声明。

class Rational { ... };

const Rational operator*(const Rational& lhs, const Rational& rhs);

很多第一次看到这些的程序员会不以为然。为什么 operator* 的结果应该是一个 const object(对象)?因为如果它不是,客户就可以犯下如此暴行:

Rational a, b, c;
...
(a * b) = c;                             // invoke operator= on the
                                         // result of a*b!

我不知道为什么一些程序员要为两个数的乘积赋值,但是我知道很多程序员这样做也并非不称职。所有这些可能来自一个简单的输入错误(要求这个类型能够隐式转型到 bool):

if (a * b = c) ...                       // oops, meant to do a comparison!

如果 a 和 b 是 built-in type(内建类型),这样的代码显而易见是非法的。一个好的 user-defined types(用户自定义类型)的特点就是要避免与 built-ins(内建类型)毫无理由的不和谐(参见 Item 18),而且对我来说允许给两个数的乘积赋值看上去正是毫无理由的。将 operator* 的返回值声明为 const 就可以避免这一点,这就是我们要这样做的理由。

关于 const parameters(参数)没什么特别新鲜之处——它们的行为就像 local(局部)的 const objects(对象),而且无论何时,只要你能,你就应该这样使用。除非你需要改变一个 parameter(参数)或 local object(本地对象)的能力,否则,确保将它声明为 const。它只需要你键入六个字符,就能将你从我们刚刚看到的这个恼人的错误中拯救出来:“我想键入 '==',但我意外地键入了 '='”。

const member functions(const 成员函数)

member functions(成员函数)被声明为 const 的目的是标明这个 member functions(成员函数)可能会被 const objects(对象)调用。因为两个原因,这样的 member functions(成员函数)非常重要。首先,它使一个 class(类)的 interface(接口)更容易被理解。知道哪个函数可以改变 object(对象)而哪个不可以是很重要的。第二,它们可以和 const objects(对象)一起工作。因为,书写高效代码有一个很重要的方面,就像 Item 20 所解释的,提升一个 C++ 程序的性能的基本方法就是 pass objects by reference-to-const(以传 const 引用的方式传递一个对象)。这个技术只有在 const member functions(成员函数)和作为操作结果的 const-qualified objects(被 const 修饰的对象)存在时才是可行的。

很多人没有注意到这样的事实,即 member functions(成员函数)在只有 constness(常量性)不同时是可以被 overloaded(重载)的,但这是 C++ 的一个重要特性。考虑一个代表文本块的类:

class TextBlock {
public:
  ...
  const char& operator[](std::size_t position) const   // operator[] for
  { return text[position]; }                           // const objects

  char& operator[](std::size_t position)               // operator[] for
  { return text[position]; }                           // non-const objects

private:
   std::string text;
};

TextBlock 的 operator[]s 可能会这样使用:

TextBlock tb("Hello");
std::cout << tb[0];                    // calls non-const
                                       // TextBlock::operator[]
const TextBlock ctb("World");
std::cout << ctb[0];                   // calls const TextBlock::operator[]

顺便提一下,const objects(对象)在实际程序中最经常出现的是作为这样一个操作的结果:passed by pointer- or reference-to-const(以传指针或者引用给 const 的方式传递)。上面的 ctb 的例子是人工假造的。下面这个例子更真实一些:

void print(const TextBlock& ctb)       // in this function, ctb is const
{
  std::cout << ctb[0];                 // calls const TextBlock::operator[]
  ...
}

通过 overloading(重载) operator[],而且给不同的版本不同的返回类型,你能对 const 和 non-const 的 TextBlocks 做不同的操作:

std::cout << tb[0];                    // fine — reading a
                                       // non-const TextBlock

tb[0] = 'x';                           // fine — writing a
                                       // non-const TextBlock

std::cout << ctb[0];                   // fine — reading a
                                       // const TextBlock

ctb[0] = 'x';                          // error! — writing a
                                       // const TextBlock

请注意这里的错误只与被调用的 operator[] 的 return type(返回类型)有关,而调用 operator[] 本身总是正确的。错误出现在企图为 const char& 赋值的时候,因为它是 const 版本的 operator[] 的 return type(返回类型)。

再请注意 non-const 版本的 operator[] 的 return type(返回类型)是 reference to a char(一个 char 的引用)而不是一个 char 本身。如果 operator[] 只是返回一个简单的 char,下面的语句将无法编译:

tb[0] = 'x';

因为改变一个返回 built-in type(内建类型)的函数的返回值总是非法的。即使它合法,C++ returns objects by value(以传值方式返回对象)这一事实(参见 Item 20)也意味着被改变的是 tb.text[0] 的一个 copy(拷贝),而不是 tb.text[0] 自己,这不会是你想要的行为。

让我们为哲学留一点时间。看看一个 member function(成员函数)是 const 意味着什么?有两个主要的概念:bitwise constness(二进制位常量性)(也称为 physical constness(物理常量性))和 logical constness(逻辑常量性)。

bitwise(二进制位)const 派别坚持认为,一个 member function(成员函数),当且仅当它不改变 object(对象)的任何 data members(数据成员)(static(静态的)除外),也就是说如果不改变 object(对象)内的任何 bits(二进制位),则这个 member function(成员函数)就是 const。bitwise constness(二进制位常量性)的一个好处是比较容易监测违例:编译器只需要寻找对 data members(数据成员)的 assignments(赋值)。实际上,bitwise constness(二进制位常量性)就是 C++ 对 constness(常量性)的定义,一个 const member function(成员函数)不被允许改变调用它的 object(对象)的任何 non-static data members(非静态数据成员)。

不幸的是,很多效果上并不是完全 const 的 member functions(成员函数)通过了 bitwise(二进制位)的检验。特别是,一个经常改变某个 pointer(指针)指向的内容的 member function(成员函数)效果上不是 const 的。除非这个 pointer(指针)在这个 object(对象)中,否则这个函数就是 bitwise(二进制位)const 的,编译器也不会提出异议。例如,假设我们有一个 TextBlock-like class(类似 TextBlock 的类),因为它需要与一个不知 string objects(对象)为何物的 C API 打交道,所以它需要将它的数据存储为 char* 而不是 string。

class CTextBlock {
public:
  ...

  char& operator[](std::size_t position) const   // inappropriate (but bitwise

  { return pText[position]; }                    // const) declaration of
                                                 // operator[]
private:
  char *pText;
};

尽管 operator[] 返回 a reference to the object's internal data(一个引向对象内部数据的引用),这个 class(类)还是(不适当地)将它声明为一个 const member function(成员函数)(Item 28 将谈论一个深入的主题)。先将它放到一边,看看 operator[] 的实现,它并没有使用任何手段改变 pText。结果,编译器愉快地生成了 operator[] 的代码,因为毕竟对所有编译器而言,它都是 bitwise(二进制位)const 的,但是我们看看会发生什么:

const CTextBlock cctb("Hello");        // declare constant object

char *pc = &cctb[0];                   // call the const operator[] to get a
                                       // pointer to cctb's data

*pc = 'J';                             // cctb now has the value "Jello"

这里确实出了问题,你用一个 particular value(确定值)创建一个 constant object(常量对象),然后你只是用它调用了 const member functions(成员函数),但是你还是改变了它的值!

这就引出了 logical constness(逻辑常量性)的概念。这一理论的信徒认为:一个 const member function(成员函数)可能会改变调用它的 object(对象)中的一些 bits(二进制位),但是只能用客户无法察觉的方法。例如,你的 CTextBlock class(类)在需要的时候可以储存文本块的长度:

class CTextBlock {
public:

  ...

  std::size_t length() const;

private:
  char *pText;
  std::size_t textLength;             // last calculated length of textblock
  bool lengthIsValid;                 // whether length is currently valid
};

std::size_t CTextBlock::length() const
{
  if (!lengthIsValid) {
    textLength = std::strlen(pText);  // error! can't assign to textLength
    lengthIsValid = true;             // and lengthIsValid in a const
  }                                   // member function

  return textLength;
}

length 的实现当然不是 bitwise(二进制位)const 的—— textLength 和 lengthIsValid 都可能会被改变——但是它还是被看作对 const CTextBlock 对象有效。但编译器不同意,它还是坚持 bitwise constness(二进制位常量性),怎么办呢?

解决方法很简单:利用以 mutable 闻名的 C++ 的 const-related(const 相关)的灵活空间。mutable 将 non-static data members(非静态数据成员)从 bitwise constness(二进制位常量性)的约束中解放出来:

class CTextBlock {
public:

  ...

  std::size_t length() const;

private:
  char *pText;

  mutable std::size_t textLength;         // these data members may
  mutable bool lengthIsValid;             // always be modified, even in
};                                        // const member functions

std::size_t CTextBlock::length() const
{
  if (!lengthIsValid) {
    textLength = std::strlen(pText);      // now fine
    lengthIsValid = true;                 // also fine
  }

  return textLength;
}

避免 const 和 non-const member functions(成员函数)的重复

mutable 对于解决 bitwise-constness-is-not-what-I-had-in-mind(二进制位常量性不太合我的心意)的问题是一个不错的解决方案,但它不能解决全部的 const-related(const 相关)难题。例如,假设 TextBlock(包括 CTextBlock)中的 operator[] 不仅要返回一个适当的字符的 reference(引用),它还要进行 bounds checking(边界检查),logged access information(记录访问信息),甚至 data integrity validation(数据完整性确认),将这些功能都加入到 const 和 non-const 的 operator[] 函数中(不必为我们现在有着非凡长度的 implicitly inline functions(隐含内联函数)而烦恼,参见 Item 30),使它们变成如下这样的庞然大物:

class TextBlock {
public:

  ...

  const char& operator[](std::size_t position) const
  {
    ...                                 // do bounds checking
    ...                                 // log access data
    ...                                 // verify data integrity
    return text[position];
  }

  char& operator[](std::size_t position)
  {
    ...                                 // do bounds checking
    ...                                 // log access data
    ...                                 // verify data integrity
    return text[position];
  }

private:
   std::string text;
};

哎呀!你是说 code duplication(重复代码)?还有随之而来的额外的编译时间,维护成本以及代码膨胀等令人头痛之类的事情吗?当然,也可以将 bounds checking(边界检查)等全部代码转移到一个单独的 member function(成员函数)(自然是 private(私有)的)中,并让两个版本的 operator[] 来调用它,但是,你还是要重复写出调用那个函数和 return 语句的代码。

你真正要做的是只实现一次 operator[] 的功能,而使用两次。换句话说,你可以用一个版本的 operator[] 去调用另一个版本。并可以为我们 casting away(通过强制转型脱掉)constness(常量性)。

作为一个通用规则,casting(强制转型)是一个非常坏的主意,我会投入整个一个 Item 的篇幅来告诉你不要使用它(Item 27),但是 code duplication(重复代码)也不是什么好事。在当前情况下,const 版本的 operator[] 所做的事也正是 non-const 版本所做的,仅有的不同是它有一个 const-qualified return type(被 const 修饰的返回类型)。在这种情况下,casting away(通过强制转型脱掉)return value(返回类型)的 const 是安全的,因为,无论谁调用 non-const operator[],首先要有一个 non-const object(对象)。否则,它不能调用一个 non-const 函数。所以,即使需要一个 cast(强制转型),让 non-const operator[] 调用 const 版本也是避免重复代码的安全方法。代码如下,你读了后面的解释后对它的理解可能会更加清晰:

class TextBlock {
public:

  ...

  const char& operator[](std::size_t position) const     // same as before
  {
    ...
    ...
    ...
    return text[position];
  }

  char& operator[](std::size_t position)         // now just calls const op[]
  {
    return
      const_cast<char&>(                         // cast away const on
                                                 // op[]'s return type;
        static_cast<const TextBlock&>(*this)     // add const to *this's type;
          [position]                             // call const version of op[]
      );
  }

...

};

正如你看到的,代码中有两处 casts(强制转型),而不是一处。我们让 non-const operator[] 调用 const 版本,但是,如果在 non-const operator[] 的内部,我们仅仅是调用 operator[],那我们将递归调用我们自己。它会进行一百万次甚至更多。为了避免 infinite recursion(无限递归),我们必须明确指出我们要调用 const operator[],但是没有直接的办法能做到这一点,于是我们将 *this 从 TextBlock& 的自然类型强制转型到 const TextBlock&。是的,我们使用 cast(强制转型)为它加上了 const!所以我们有两次 casts(强制转型):第一次是为 *this 加上 const(以便在我们调用 operator[] 时调用它的 const 版本),第二次是从 const operator[] 的 return value(返回值)之中去掉 const。

加上 const 的 cast(强制转型)仅仅是强制施加一次安全的转换(从一个 non-const object(对象)到一个 const object(对象)),所以我们用一个 static_cast 来做。去掉 const 只能经由 const_cast 来完成,所以在这里我们没有别的选择。(在技术上,我们有。一个 C-style cast(C 风格的强制转型)也能工作,但是,就像我在 Item 27 中解释的,这样的 casts(强制转型)很少是一个正确的选择。如果你不熟悉 static_cast 或 const_cast,Item 27 中包含有一个概述。)

在完成其它事情的基础上,我们在此例中调用了一个 operator(操作符),所以,语法看上去有些奇怪。导致其不会赢得选美比赛,但是它根据 const 版本的 operator[] 实现其 non-const 版本而避免 code duplication(代码重复)的方法达到了预期的效果。使用丑陋的语法达到目标是否值得最好由你自己决定,但是这种根据 const member function(成员函数)实现它的 non-const 版本的技术却非常值得掌握。

更加值得掌握的是做这件事的反向方法——通过用 const 版本调用 non-const 版本来避免重复——是你不能做的。记住,一个 const member function(成员函数)承诺绝不会改变它的 object(对象)的逻辑状态,但是一个 non-const member function(成员函数)不会做这样的承诺。如果你从一个 const member function(成员函数)调用一个 non-const member function(成员函数),你将面临你承诺不会变化的 object(对象)被改变的风险。这就是为什么使用一个 const member function(成员函数)调用一个 non-const member function(成员函数)是错误的,object(对象)可能会被改变。实际上,那样的代码如果想通过编译,你必须用一个 const_cast 来去掉 *this 的 const,这是一个显而易见的麻烦。而反向的调用——就像我在上面用的——是安全的:一个 non-const member function(成员函数)对一个 object(对象)能够为所欲为,所以调用一个 const member function(成员函数)也没有任何风险。这就是为什么 static_cast 在这种情况下可以工作在 *this 上的原因:这里没有 const-related 危险。

就像在本 Item 开始我所说的,const 是一件美妙的东西。在 pointers(指针)和 iterators(迭代器)上,在 pointers(指针),iterators(迭代器)和 references(引用)涉及到的 object(对象)上,在 function parameters(函数参数)和 return types(返回值)上,在 local variables(局部变量)上,在 member functions(成员函数)上,const 是一个强有力的盟友。只要可能就用它,你会为你所做的感到高兴。

Things to Remember

  • 将某些东西声明为 const 有助于编译器发现使用错误。const 能被用于任何 scope(范围)中的 object(对象),用于 function parameters(函数参数)和 return types(返回类型),用于整个 member functions(成员函数)。
  • 编译器坚持 bitwise constness(二进制位常量性),但是你应该用 conceptual constness(概念上的常量性)来编程。(此处原文有误,conceptual constness为作者在本书第二版中对 logical constness 的称呼,正文中的称呼改了,此处却没有改。其实此处还是作者新加的部分,却使用了旧的术语,怪!——译者注)
  • 当 const 和 non-const member functions(成员函数)具有本质上相同的实现的时候,使用 non-const 版本调用 const 版本可以避免 code duplication(代码重复)。
分数间隔均衡器的基本原理及应用(使用Matlab) 通过对接收信号进行加权求和,分数间隔均衡器可以抵消信号在传输过程中受到的频率响应失真。Matlab提供了丰富的工具和函数,可以方便地实现分数间隔均衡器,并进行性能评估。分数间隔均衡器(Fractional Interval Equalizer)是一种数字信号处理技术,用于均衡带通信道中的信号失真。分数间隔均衡器的基本原理是通过对接收信号进行加权求和,以抵消信号在传输过程中受到的频率响应失真。分数间隔均衡器在通信系统中具有广泛的应用。最后,我们通过比较均衡后的信号和发送数据的阈值来进行解调,并计算误码率。 阅读详情

相关推荐

5G MBS(组播广播服务)深度解析:从标准架构到商用实践

5G MBS(组播广播服务)深度解析:从标准架构到商用实践

大家好,我是码农老gou,一名专注于编程技术的程序员。在这个博客中,我将与大家分享我在工作和学习中的经验、见解以及一些实用的技术干货。 1084

[翻译] Effective C++, 3rd Edition, Item 27: 最少化 casting(强制转型)(下)

(点击此处,接上篇)在深入探究 dynamic_cast 的设计意图之前,值得留意的是多数 dynamic_cast 的实现都相当慢。例如,至少有一种通用的实现部分地基于对 class names(类名)进行 string comparisons(字符串比较)。如果你在位于一个 single-inheritance hierarchy(单继承体系)四层深处的一个 object 上执行 dyna

艺术编程 2677

Unity游戏开发必备:HybridCLR热更新实战教程(含iOS/Android适配指南)

本文详细介绍了HybridCLR在Unity游戏开发中的热更新实战教程,包括iOS/Android平台适配指南。通过环境配置、项目结构规划、热更新工作流实现等步骤,帮助开发者高效掌握C#热更新技术,解决移动游戏快速迭代的痛点问题。

weixin_29011395的博客 441

[翻译] Effective C++, 3rd Edition, Item 31: 最小化文件之间的 compilation dependencies(编译依赖)(上)

Item 31: 最小化文件之间的 compilation dependencies(编译依赖)作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/你进入到你的 C++ 程序中,并对一个 class 的 implementation(实现)进行了细微的改变。提醒你

艺术编程 4619

别再凭感觉了!手把手教你用Excel搞定激光器MPE与NOHD安全计算(附模板下载)

本文详细介绍了如何利用Excel自动化计算激光器的MPE(最大允许照射量)和NOHD(标称眼危害距离),提升激光安全评估的效率和准确性。通过实战案例和模板下载,帮助工程师快速掌握GB/T 7247标准中的关键计算,避免常见错误,适用于激光测距仪等设备的安评工作。

weixin_30954265的博客 468

CMake安装grpc生成gRPCTargets.cmake文件

以下是安装语句: cd grpc_folder git submodule update --init cmake .. make -j 4 sudo make install 然而编写依赖gRPC的程序,发现无法调用gRPC的CMake文件,错误提示如下所示: include could not find load file: /usr/local/lib/cmake/g...

2331

Effective C++ 3nd 读书摘要(Third Edition 55 Specific Ways to Improve Your Programs and Design)

一、让自己习惯C++ Item1. 视C++为一个语言联邦 C++由以下部分组成:C、Object-Oriented C++、Template C++、STL Item2. 尽量以const、enum、inline代替#define Item3. 尽可能使用const 1.一共有这样几种const用法:char greeting[] = "Hello";

thy专栏 1302

用面包板实测MOSFET:手把手教你测量跨导和开关时间

本文详细介绍了如何使用面包板、Arduino和示波器搭建测试平台,实测MOSFET的跨导和开关时间。通过分步测量流程和示波器技巧,帮助电子设计初学者深入理解MOSFET的动态特性,包括跨导计算、开关时间参数解析以及米勒平台现象分析。

beer8的博客 223

Effective系列经典著作,铺就程序员殿堂之路

Effective系列图书是我们学习中难以绕开的经典。经典书籍需要不断被重读,因为每一次重读都会带来新的体会。本文带来了5本Effective系列的经典著作,让我们一同迎着大师们的智慧之光,走向卓越。 1 《Effective C++:改善程序与设计的55个具体做法(第三版)中文版(双色)》 Effective C++: 55 Specific Ways to Improve Your Programs and Designs,3/e 【美】Scott Meyers 著 侯捷 译 有人说C++程序员可以

博文视点(北京)官方博客 1044

OpenClaw-遇到的问题- FailoverError: Model context window too small (4096 tokens). Minimum is 16000

摘要:OpenClaw配置文件中deepseek-reasoner模型的上下文窗口被默认设置为4096 tokens(实际支持128K),导致系统报错"Model context window too small"。解决方案是删除配置文件中的contextWindow和maxTokens字段,让系统自动获取模型真实能力,并重启OpenClaw服务。配置文件通常位于用户目录下的.openclaw/openclaw.json中。

直到世界的尽头 4165

VS code 调试显示preLaunchTask“g++“已终止,退出代码为-1。 生成‘... .exe‘文件错误

总结: 以后每个工程都需要按照这样的流程:创建一个.c/.cpp文件 -> F5运行 -> 配置tasks.json文件-> launch.json文件 -> 运行

qq_45151652的博客 2516

博客摘录「 [翻译] Effective C++, 3rd Edition, Item 3: 只要可能就用 const」2025年2月27日

因为改变一个返回 built-in type(内建类型)的函数的返回值总是非法的。

qq_44644334的博客 191

深入剖析 Wenet 下 AISHELL-1 的 run.sh 脚本:从数据准备到模型训练

run.sh脚本用于处理 AISHELL-1 数据集的整个训练和测试流程。我们将逐步分析脚本中的每个部分,包括环境变量、参数设置、各个阶段的执行等。stage: 设置脚本的起始阶段(如数据准备)。stop_stage: 设置脚本的结束阶段。通过对 Wenet 下 AISHELL-1 的run.sh脚本的详细剖析,我们希望你能更好地理解每个步骤的功能及其背后的原理。此脚本不仅为模型训练提供了便利,也为用户自定义和扩展训练流程提供了基础。希望本教程能帮助你顺利完成语音识别项目!

帅小柏的博客 1634

博客摘录「 [翻译] Effective C++, 3rd Edition, Item 3: 只要可能就用 const」2025年2月26日

一个 const member function(成员函数)不被允许改变调用它的 object(对象)的任何 non-static data members(非静态数据成员)。

qq_44644334的博客 199

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

[翻译] Effective C++, 3rd Edition, Item 2: 用 consts, enums 和 inlines 取代 #defines

Item 2: 用 consts, enums 和 inlines 取代 #defines作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/这个 Item 改名为“用 compiler(编译器)取代 preprocessor(预处理器)”也许更好一些,因为 #d

艺术编程 6553

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

[翻译] Effective C++, 3rd Edition, Item 28: 避免返回 object 内部构件的 "handles"(“句柄”)

Item 28: 避免返回 object 内部构件的 "handles"(“句柄”)作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/假设你正在一个与矩形有关的应用程序上工作。每一个矩形都可以用它的左上角和右下角表示出来。为了将一个 Rectangle obje

艺术编程 3126

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

[翻译] Effective C++, 3rd Edition, Item 26: 只要有可能就推迟 variable definitions(变量定义)

Item 26: 只要有可能就推迟 variable definitions(变量定义)作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/只要你定义了一个带有 constructor(构造函数)或 destructor(析构函数)的类型的变量,当控制流程到达变量

艺术编程 2826

[翻译] Effective C++, 3rd Edition, Item 5: 了解 C++ 为你偷偷地加上和调用了什么函数

Item 5: 了解 C++ 为你偷偷地加上和调用了什么函数作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/一个 empty class(空类)什么时候将不再是 empty class(空类)?答案是当 C++ 搞定了它。如果你自己不声明一个 copy con

艺术编程 5208

[翻译] Effective C++, 3rd Edition, Item 20: 用 pass-by-reference-to-const(传给 const 引用)取代 pass-by-value(传值)(上)

Item 20: 用 pass-by-reference-to-const(传 const 引用)取代 pass-by-value(传值)作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/缺省情况下,C++ 以 by value(传值)方式将 objec

艺术编程 4476

[翻译] Effective C++, 3rd Edition, Item 20: 用 pass-by-reference-to-const(传 const 引用)取代 pass-by-value(传值)(下)

(点击此处,接上篇)如如果你掀开编译器的盖头偷看一下,你会发现 references(引用)一般是作为 pointers(指针)实现的,所以以 by reference(传引用)方式传递某物实际上通常意味着传递一个 pointer(指针)。由此可以得出结论,如果你有一个 built-in typ(内建类型)的 object(例如,一个 int),以 by value(传值)方式传递它常常比 b

艺术编程 2704

[翻译] Effective C++, 3rd Edition, Item 24: 当希望将 type conversions(类型转换)应用于所有 parameters(参数)时,请声明为 non-member functions(非成员函数)

Item 24: 当希望将 type conversions(类型转换)应用于所有 parameters(参数)时,请声明为 non-member functions(非成员函数)作者:Scott Meyers 译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/在本书的 Introducti

艺术编程 4441

[翻译] Effective C++, 3rd Edition, 附录 A. 超越 Effective C++

附录 A. 超越 Effective C++作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/Effective C++ 覆盖了我认为对当前的 C++ 程序员最重要的通用指导方针,但是如果你有兴趣在更多的方面提升你的效力,我推荐你去研读我的其他 C++

艺术编程 6445

[翻译] Effective C++, 3rd Edition, 附录 B. 第二和第三版之间的 Item 映射

附录 B. 第二和第三版之间的 Item 映射作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/Effective C++ 的第三版在很多方面与第二版不同,其中最引人注目的是它包含很多新的信息。然而,第二版的大部分内容依然保留在第三版中,虽然经常会改变形式和位置

艺术编程 5819

[翻译] Effective C++, 3rd Edition, Item 30: 了解 inlining(内联化)的来龙去脉(上)

Item 30: 了解 inlining(内联化)的来龙去脉作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/inline functions ——多么棒的主意啊!它们看起来像 functions,它们产生的效果也像 functions,它们在各方面都比 mac

艺术编程 5420

《effective c++(第三版)》55个具体做法1

做法1

程序的尽头是数学,一日不推导赶不上买买提 210

C# 世界的《Effective C++》,来自.Net之父的核心揭秘

作为.NET程序员,如果只读三本书,那么《框架设计指南:构建可复用.NET库的约定、惯例与模式(第3版)》这本书一定不容错过!

博文视点(北京)官方博客 1137
上一篇: [翻译] Effective C++, 3rd Edition, Item 2: 用 consts, enums 和 inlines 取代 #defines
下一篇: [翻译] Effective C++, 3rd Edition, Item 4: 确保 objects(对象)在使用前被初始化
fatalerror99
博客等级 码龄24年 166粉丝 20原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值