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++默默编写并调用哪些函数

  • 相关阅读:
    hdu1238 Substrings
    CCF试题:高速公路(Targin)
    hdu 1269 迷宫城堡(Targin算法)
    hdu 1253 胜利大逃亡
    NYOJ 55 懒省事的小明
    HDU 1024 Max Sum Plus Plus
    HDU 1087 Super Jumping! Jumping! Jumping!
    HDU 1257 最少拦截系统
    HDU 1069 Monkey and Banana
    HDU 1104 Remainder
  • 原文地址:https://www.cnblogs.com/izcat/p/13551063.html
Copyright © 2011-2022 走看看