zoukankan      html  css  js  c++  java
  • Effective C++笔记(更新中...)

    Effective C++

    55 Specific Ways to Impove Your Programs and Designs

    改善程序与设计的55个具体做法

    豆瓣读书

    1. Accustoming Yourself to C++ 让自己习惯C++

    01: View C++ as a federation of languages

    视C++为一个语言联邦

    C++最初名称 C with Classes

    主要的次语言有四个:

    • C:区块、语句、预处理、数据类型、数组、指针
    • Object-Oritented C++:对象、封装、继承、多态、动态绑定
    • Template C++ :泛型编程
    • STL:容器、迭代器、算法、函数对象

    每个次语言有自己的规约

    02: Prefer consts, enums and inlines to #defines

    尽量以const,enum,inline替换#define

    • 以编译器替换预处理器,因为 #define不是语言的一部分,编译器看不到被#define定义的变量,没有进入记号表(symbol table),编译错误时被替换,难以追踪问题

    • 指针类型常量,写两次const: const char* const authorName = "Scott Meyers";

      最好使用string类型:const std::string authorName(“Scott Meyers”)

    • 类常量,声明时获取初值:static const

    • 旧编译器不支持声明获取初值,使用enumenum {Num = 5};

      enum类似#define,无法获取常量地址,没有分配存储空间

      模板元编程

    • template inline函数替换宏,使用宏注意加括号,但仍无法避免MAX(++a, b)形式调用的不确定结果

    • #incline需求降低了,但没有完全消除。#ifdef/#ifndef控制编译

    03: Use const whenever possible.

    尽可能使用const

    • const与指针

      • 星号左边,const char* p:被指物是常量(*p)
      • 星号右边,char* const p:指针自身是常量(p)
      • 星号两边,const char* const p:被指物和指针都是常量
      • const Widget* pw 与 Widget const *pw相同
    • const与函数

      • 返回值 const
      • const参数:除非需要修改参数
      • const成员函数:不改动对象
    • bitwise/phisical constness与logical constness

      class CTextBook {
      public:
          ...
          std::size_t length() const 
          {
              if (!isLengthValid) {
                  textLength = std::strlen(pText);  // 错误:在只读结构中不能有赋值操作
                  isLengthValid = true;
              }
      
              return textLength;
          }
      private:
          char*       pText;
          std::size_t textLength;
          bool        isLengthValid;
      };
      
      • logical constness实现方式,使用mutable释放掉non-static成员变量的bitwise constness约束:
      class CTextBook {
      public:
          ...
          std::size_t length() const 
          {
              if (!isLengthValid) {
                  textLength = std::strlen(pText);
                  isLengthValid = true;
              }
      
              return textLength;
          }
      private:
          char*  		  pText;
          mutable std::size_t textLength;
          mutable bool 	  isLengthValid;
      };
      
      • 如果operator[]包含边界检查、访问信息、数据完整性检验,编写const和non-const方法会有大量重复代码

        将相同代码移动到同一个成员函数,多了函数调用和两次return

      • 常量性转移(casting away constness)

        non-const调用const函数

      class TextBook {
      public:
          ...
          char& operator[](std::size_t position) const  // const对象
      	{
      		... // 边界检查
      		... // 访问信息
      		... // 数据完整性检验
      		return text[position];
      	}
      	char operator[](std::size_t position)
      	{
      		return const_cast<char&>(				// 将operator[]返回值的const转除
      			static_cast<const TextBook&>(*this)[position]	// 为*this加上const,调用const函数
      		);
      	}
      private:
          std::string text;
      };
      
      • 不能反向进行,即让const版本函数调用non-const函数。调用const方法必须保证对象的逻辑状态不被修改,non-const有更改的风险

    04: Make sure that objects are initialized before they’re used

    确定对象被使用前已先被初始化

    读取未初始化的值会导致不明确的行为:某些平台可能会让程序终止运行;读入半随机的bits,导致不可预知的程序行为

    • 内置类型,定义时手动初始化/读入

      int x = 0;

      const char* text = “A C-style string”;

      double d;

      std::cin >> d;

    • 赋值(assignment)与初始化(initialization)

      错误做法,构造函数采用了赋值而非初始化:

      class PhoneNumber { ... };
      class Entry {
      private:
          std::string theName;
          std::string theAddress;
          std::list<PhoneNumber> thePhones;
          int numCount;
      public:
          Entry(const std::string& name, const std::string& address,
              const std::list<PhoneNumber>& phones);
      }
      
      Entry::Entry(const std::string& name, const std::string& address,
              const std::list<PhoneNumber>& phones)
      {
          theName = name;
          theAddress = address;
          thePhones = phones;
          numCount = 0;
      }
      
    • C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。使用成员初始化列表:

      Entry::Entry(const std::string& name, const std::string& address,
              const std::list<PhoneNumber>& phones)
          :theName(name),
          theAddress(address),
          thePhones(phones),
          numCount(0)
      { }
      

      结果与上面相同,但效率更高

      default构造函数+调用copy赋值操作符 VS 调用一次copy构造函数

      • 赋值的版本,首先调用默认构造函数,为theName,theAddress,thePhones设初值,然后再赋新的值

      • 初始化列表使用copy构造函数,(参数可为空,调用默认构造函数)

    • 成员变量是const或reference,一定要使用初值

    • 成员初始化总是以声明次序被初始化,与初始化列表顺序无关

    • non-local static对象的初始化顺序,C++无明确定义

      至少两份源码文件,在其中一次引用静态对象,使用前可能未初始化

      解决方法:单例模式

      将每个non-local static对象在专属函数内声明为static,函数返回其引用指向的对象。用户调用函数而不是直接使用对象,转化为local static对象。tfs ==> tfs()

      class FileSystem { ... };
      FileSystem tfs()
      {
      	static FileSystem fs;
      	return fs;
      }
      
      // 客户使用
      class Directory { ... };
      Directory::Directory( params )
      {
      	...
      	std::size_t disks = tfs().numDisks();
      	...
      }
      

      额外问题:非const的静态对象,多线程中的竞争条件(race conditions),即多个线程同时访问相同的共享数据,造成数据的不一致性

    2.Constructors, Destructors, and Assignment Operators 构造/析构/赋值运算

    05: Know what functions C++ silently writes and calls

    了解C++默默编写并调用哪些函数

  • 相关阅读:
    友盟统计,监听事件次数。
    webView 加载网页
    Springboot 启动时Bean初始化,启动异常-Assert.isTrue(condition,message) 报错
    Springboot使用@ConfigurationProperties注解 配置读不进去
    2018即将结束,给寒假李哥flag
    大精度求和,给任意两个数 m,n 甚至m,n->∞ 计算x+y
    第二章JavaScript 函数和对象
    第三章JavaScript 内置对象
    响应式网页设计
    新的页面布局方式
  • 原文地址:https://www.cnblogs.com/izcat/p/13551063.html
Copyright © 2011-2022 走看看