首页 > 编程语言 >[Effective] 1 让自己习惯 C++

[Effective] 1 让自己习惯 C++

时间:2023-03-07 19:25:28浏览次数:41  
标签:std 初始化 const 变量 Effective 习惯 C++ 指针 函数

1 让自己习惯 C++

条款 01:视 C++ 为一个语言联邦

C++ 可以认为由 4 个次级语言组合而成:C 是 C++ 的语法基础;Object-oriented C++ 实现面向对象设计;Template C++ 实现泛型编程;STL 提供容器、迭代器、算法等。

每个次级语言都有自己的规约,为了使效率尽可能提高,需要遵守每个部分的规约。

条款 02:尽可能以 const, enum, inline 替换 #define

使用 #define 预定义变量将导致无法跟踪错误来源,而且变量会在替换过程中被复制多份。

定义常量指针时需要写两次 const,保证指针指向的内存空间数值不被更改,同时保证指针指向的内存空间不被修改。

class 中的专属常量需要在声明时加上 static 关键字以保证常量只有一份实体。

如果 class 中专属常量为整数类型,则可以在声明时定义,否则需要额外定义,比如

// Header file
class Example {
    static const int m = 5;
    // use enum { m = 5 }; if not allowed
    static const double n;
}

// Implement file
const double Example::n = 5.0;

如果编译器不允许在声明时定义整型变量,则可以使用 enum 充当整型。

使用 template inline 函数代替 #define 宏函数,即使给每个变量加上括号,宏函数仍然有可能出现错误。

// Don't
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))

// Do
template<typeName T>
inline void call_with_max(const T &a, const T &b) {
    f(a > b ? a : b);
}

对于简单变量,用 const 对象或 enum 替换 #define;对于宏函数,用 inline 函数替换 #define

条款 03:尽可能使用 const

如果 const 出现在 * 左边,则变量不变;如果 const 出现在 * 右边,则指针指向不变。

void f1(const Widget* pw);void f2(Widget const * pw); 的含义相同。

声明迭代器 iteratorconst 等同于声明 T* const 指针,此时可以修改指针指向的值但不可修改指针指向的内存空间。如果不希望迭代器修改值,即模拟成 const T* 指针,则需要声明 const_iterator

函数返回常量值可以避免一些因为赋值产生的错误。

将成员函数设置为 const 后它不可以修改成员变量,它可以使接口更容易理解,也可以提升运行效率。

如果两个成员函数只是 const 和非 const 的区别,它们可以被重载,比如

class TextBlock {
public:
    const char& operator[] (std::size_t position) const { return text[position]; }
    char& operator[] (std::size_t position) { return text[position]; }
private:
    std::string text;
}

const 成员函数确实不允许修改成员变量,但当成员为指针时,不修改指针的指向,但可以修改指针所指内存空间的值。

如果有必要在 const 成员函数中修改成员变量的值,则需要声明成员变量为 mutable

可以通过转型来避免写两个函数体相同但 const 修饰不同的函数,以上面的函数为例

class TextBlock {
public:
    const char& operator[] (std::size_t position) const {
        ...
        return text[position];
    }
    char& operator[] (std::size_t position) {
        return const_cast<char&>(
            static_cast<const TextBlock&>(*this)[position]
        )
    }
private:
    std::string text;
}

static_cast 将普通对象转换为 const 对象,再由 const_cast 移除 const

const 函数调用非 const 函数是一种错误行为,因为对象有可能被调用的非 const 函数修改。

将某些变量、参数、返回类型或成员函数本体声明为 const 可以帮助编译器检测错误;编译器强制要求 const 成员函数不修改成员变量,但可以修改指针成员指向内存空间的值;当 const 和非 const 函数有等价的实现时,可以使非 const 函数调用 const 函数以避免代码重复。

条款 04:确定对象被使用前已先被初始化

读取未初始化的值可能导致程序终止运行或读取到正在使用的对象内存的数值,进而污染该对象导致不确定的结果。

使用正确的方式初始化对象,而不是赋值,比如

class PhoneNumber { ... };
class ABEntry {
public:
	ABEntry(const std::string &name, const std::list<PhoneNumber> &phones);
private:
    std::string theName;
    std::list<PhoneNumber> thePhones;
    int numTimesConsulted;
}

// Don't
ABEntry::ABEntry(const std::string &name, const std::list<PhoneNumber> &phones) {
    theName = name;
    thePhones = phones;
    numTimesConsulted = 0;
}

// Do
ABEntry::ABEntry(const std::string &name, const std::list<PhoneNumber> &phones)
    :theName(name), thePhones(phones), numTimesConsulted(0) { }

// Default
ABEntry::ABEntry()
    :theName(), thePhones(), numTimesConsulted(0) { }

赋值方法会先调用 default 构造函数为每个成员变量赋初值,然后再立刻赋予新的值,而初始化方法可以剩下 default 构造函数的操作直接初始化各变量,提高运行效率。这个方法也适用于自己构造 default 构造函数。

成员初始化的顺序总是与类成员变量声明顺序一致,而不是构造函数参数的顺序。

如果成员变量是 const 变量或引用变量,则它们必须被初始化而不能被赋值。

调用其他库中 static 变量时,无法保证该变量已经被正确地初始化,可以使用返回该静态变量的函数代替直接用 extern 变量的方法,类似于单一实例设计模式,避免这个问题。但是任何非 conststatic 对象在多线程环境下都会导致初始化次序问题,最好的解决办法是在程序的单线程启动阶段手工调用所有返回引用的函数。

对内置类型手工初始化,C++ 不保证使用时已被正确初始化;构造函数使用成员初值列,且排列次序应该与在 class 中的声明顺序一致;用本编译单元中的 static 对象替换非本编译单元的 static 对象。

标签:std,初始化,const,变量,Effective,习惯,C++,指针,函数
From: https://www.cnblogs.com/futureknight/p/17189268.html

相关文章

  • 【C++】 结构化绑定“需要编译器标志“/std:c++17“
    问题  vs打开工程时报错:"结构化绑定"需要编译器标志"/std:c++17"  解决  ......
  • 蓝桥-13届-C++-B组-省赛-I题-李白打酒加强版
    最直接的做法,算是回溯吧:生成指定01数的序列挨个检查是否满足题意并计数能不能将生成和判断的过程统一呢?能不能记忆前面的序列呢瞄一眼题解,往动态规划的方向上靠#inc......
  • C++笔记-指针
    1.const指针和指向const的指针指向const的指针是在类型前加星号可以指向非const类型指针可以改变指向dereference不能改变值const指针是在类型后面加星号指针不可......
  • C++笔记-static本地变量
    static本地变量只能被本地看到,所以不同函数之间的static变量相同也没事,但是同一个函数调用多次会忽略后面的初始化。#include<iostream>voidmyStaticFunction(){......
  • c++笔架-编译,头文件,链接
    编译是按任意顺序进行的,并且每个文件是独立编译的,所以如果不用头文件的话,其他文件中定义的函数,在当前文件中是不可见的。也就是说没有定义。头文件只包含declaration,不包......
  • C++笔记-函数指针
    函数指针语法://fcnPtrisapointertoafunctionthattakesnoargumentsandreturnsanintegerint(*fcnPtr)();特点:函数指针的类型(参数和返回值)都必须和......
  • C++ primer 智能指针的陷阱
    1.不使用相同的内置指针值初始化(或reset)多个智能指针有一个现成的约定是当我们将一个原生指针交给(具有资源所有权的)智能指针的时候,这个智能指针被允许认为自己暂时获得......
  • C++中的静态多态和动态多态
    今天学习C++时,发现C++中存在静态多态和动态多态静态多态=>也称为编译期多态=>基于模板编程的静态多态动态多态=>也称为运行期多态 =>面向对象的动态多态,它基......
  • 06、C++指针常量与常量指针
    constint*a;//指向常量int的指针int*consta;//指向int的常量指针,指针是常量voidf1(int*consta){//指针是常量std::cout<<*a<<std::endl;}voidf......
  • 02、C++字符串编码转换
    //string<——>wstring#include<codecvt>std::stringwstring2utf8string(conststd::wstring&str){staticstd::wstring_convert<std::codecvt_utf8<wchar_t>......