Effective c++读书笔记(1)

条例一:视c++为一个语言联邦

将c++视为一个由相关语言组成的联邦

c:c++语言包容c的语法以c为基础,在很多时候,c++是c的高级解法。

面向对象:c++内包含封装、继承、多态、虚函数等。

模板:c++的泛型编程部分。

STL:stl是标准模板库。

将c++视为由上述四个次语言构成的联邦。

条例二:尽量用inline、enum、const替换#define

本条款的含义是尽量使用编译器来替换预处理器。

#define ASPECT 1.68

在其出错时,它可能会出现1.68而不是ASPECT,这会导致调试的困难。因为该名称可能未进入信号表。应该改为常量表示。

const double Aspect=1.68

两种特殊情况

在宏定义一个字符串时,我们不能只简单的将define替换为const

#define MYCHARARR helloworld
const char * Mychararr="helloworld";//错误,常量指针的指向可以改变

const char const * Mychararr="helloworld"; //正确,但不完美

const string Mychararr="helloworld"; //完美

第二种情况是在定义一个类的专属常量时

class myclass
{
    private: static const int Num=5;//声明式
               int arr[Num];
};
//如果不取地址,上面声明式即可,若取得一个专属常量的地址,则需定义
const int myclass::Num; //不赋值  定义式

不能用#define定义一个专属的class常量,因为#define一旦定义,在编译过程有效,而非仅仅属于一个类,不存在private:#define的语法。然而也有许多编译器不支持这种写法。

于是引入enum类型。

class myclass
{
    private: enum{Num=5};
               int arr[Num];
};

enum比较像#define,例如取const类型是合法的,取#define和enum类型是不合法的。enum类型不会导致非必要的类型分配。

另一个#define的误用情况是以它来实现宏,这种方式不会有函数调用的额外开销

#define MAX(a,b) f((a)>(b)?(a):(b))
int a=5,b=0;
MAX(++a,b);//递增一次
MAX(++a,b+10);//递增两次

该方式有很多问题,首先要为宏的所有实参加上小括号,还有很多问题。一般我们可以使用函数模板和inline函数来实现相同的结果。

对于单纯常量,最好以const或enum来替换#define

形似函数的宏,尽量使用内联函数来替代。

条款三:尽可能使用const

const 指定一个不被改动的对象

char p[]="helloworld" //字符串数组

char* p="helloworld" //等于上行

const char * p="helloworld" //指向可以改变,值不能改变 

char* const p="helloworld" //指向不能改变,内容可以改变

const char * const p="helloworld" //值和指向都不能改变

下面两种const代表含义均相同

void fun(const Widget* pw)

void fun(const Widget *pw)

在STL中,迭代器是根据指针塑膜出来的,因此声明迭代器为const就和声明指针为const有相似的问题。

const vector<int>::iterator //表明迭代器不可变,指向的内容可以改变

vector<int>::const_iterator //迭代器可变,指向的内容不可变

在函数声明时,const可以和函数返回值、函数参数、函数自身产生关联

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

在上述函数声明中,返回值为const,这可以避免a*b=c这种不合法的操作。

const成员函数

将const用于成员函数的目的是为了确认该成员函数可作用于const对象身上。

这一类成员函数之所以重要,

理由一:可以得知那个函数可以改变对象内容而哪个不行。

理由二:使操作const对象成为可能。

const不同,函数可重载。

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

TextBlock tb("hello");
cout<<tb[0]; //非const

const TextBlock ctb("hello");
cout<<tb[0]; //const

tb[0]='x' //正确

ctb[0]='x' //错误

另外,返回类型必须为引用,否则tb[0]=‘x' 无法通过编译。因为如果返回值类型为一个内置类型,改动函数的返回值不合法,因为其是一个右值。

即使合法,改动的也是其副本,而非其本身。

bitwise const 成员函数只有在不更改的任何成员变量(static除外)才能被称作const

char& opertator[](size_t position) const
{ return pText[position];
}
//bitwise声明,但其实不合适
const CTextBlock cctb("hello");
char *pc=&cctb[0];
*pc='p';//更改成功

还是改变了其内的值。(若不想改变,需要给返回值也加上const)

所以引出

logical const 一个const成员函数可以修改可以修改他所处理对象的某些bits,但只有在客户端侦测不出来的情况下才可如此。

class CTextBlock{
public:
            size_t length()const;
 private: 
            bool lengthisvalid;
            size_t textlenth;
            char * pText;

  };
size_t CTextBlock::length()const
{
    if(!lenthisvalid)
    {textlenth=strlen(pText);
        lenthisvaild=true;
    }
    return textlenth;
}

如上述代码,lenthisvaild和textlenth都有可能被修改,不是bitwise,但是编译不会通过。

一个解决方法是将该两个变量声明为mutable。

class CTextBlock{
public:
            size_t length()const;
 private: 
            mutable bool lengthisvalid;
            mutable size_t textlenth;
            char * pText;

  };
size_t CTextBlock::length()const
{
    if(!lenthisvalid)
    {textlenth=strlen(pText);
        lenthisvaild=true;
    }
    return textlenth;
}

该声明含义是即使在const函数中,其也是可修改的。

另外,如果在operator[]中做了许多事情,如边界检测、记录访问信息等等操作,使用const或non-const成员函数可能会造成大量的代码重复,如下:

class TextBlock{
public:

    const char & operator[](size_t position)const
    {
        ...//边界检测
        ...//数据记录
        ...
        return text[position];    
    }
    char & operator[](size_t position)
    {
        
        ...//边界检测
        ...//数据记录
        ...
        return text[position];    
    }
};

就会带来代码膨胀、编译时间等等各种问题。

正确的解决方式是实现operator[]一次,并使用其两次,如下

class TextBlock{
public:

    const char & operator[](size_t position)const
    {
        ...//边界检测
        ...//数据记录
        ...
        return text[position];    
    }
    char & operator[](size_t position)
    {
        
       return 
        const_cast<char&>(  //去掉返回的const属性
            static_cast<const TextBlock&>(*this)[position]) ;  //加上const,调用上述函数
    }
};

总结:

  • 某些东西声明为const可以帮助编译器侦测错误。const可被用于任何作用域内的对象、函数参数 、函数返回值类型、成员函数本体。
  • 编译器强制实行bitwise,,但在我们使用时应该使用logical const。
  • const 和non-const有着等价操作时,可以用non-const函数调用const,避免重复。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值