Effective c++笔记----const

1. 用const对象或者enums替换#defines

1.const与define

原来写法 现在写法 原因
const char* const authorName=“ScottMeyers” const std::string authorName(“Scott Meyers”)
#define ASPECT_RATIO 1.653 const double AspectRatio = 1.653 因为宏没有计入符号表

2. class的专属const

class GamePlayer
{
private:
	static const int NUMTurns = 5;
	int socres[NumTurns];
	}

此时是不能对NUMTurns取地址,如果要取地址,则要在class的cpp文件中定义const int GamePlayer::NUMTurns,为什么不能用#define呢?因为define无视作用域,当定义之后全局有效,除非是#undef,不具有封装性,NUMTurns只有在int char bool时,才能在class内部赋值!
当需要在class内部赋值时,可以用enum,枚举约等于#define,无法取地址

class GamePlayer
{
private:
	enum{ NUMTurns = 5};
	int socres[NumTurns];
	}

3. inline函数

一般宏定义函数为

#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))

可以替换为

template<class T>
inline void callWithMax(const T&a, const T&b)
{
	f(a>b? a:b);
}

2. 尽量多使用const

2.1 const在*的左边还是右边来区分const指针和const对象

char greeting[] = "hello";
char* p = greeting;
const char* p = greeting; //对象常量
char* const p = greeting; // 指针常量
const char* const p = greeting; //const pointer const data

void f1(const Widget* pw);
void f2(Widger const* pw); //都是const dara

Effective c++笔记----const_第1张图片
上述如果是const_iterator cIter, 那么*cIter++,运行顺序是先cIter++,然后取 *cIter,因为是对象,重载了

2.2 const修饰函数返回值

  1. T f(): 返回一般的类类型,返回的类类型不能作为左值,但返回的类类型可以直接调用成员函数来修改,如function().set_Value(); 返回类类型调用复制构造函数。
  2. const T f(): 此种类型与上述第一种相同,唯一不同的是返回的类类型不能调用成员函数来修改,因为有const限定符。
  3. T& f(): 返回类的引用可以作为左值,并且返回的类类型引用可以直接调用成员函数来修改,返回的类类型不会调用复制构造函数。
  4. const T& f(): 不能作为左值,不能调用成员函数修改,不会调用复制构造函数。

2.3 const成员函数(mutable)

const修饰类中的成员函数时,表示对象是const,可以利用这个来进行函数重载
Effective c++笔记----const_第2张图片
Effective c++笔记----const_第3张图片
注意const对象一般是passed by reference to const或者pointer to const,那么该对象中调用的函数必须也是const的才行!
见fun2 !

class TextBox
{
    private:
        std::string text;

    public:
        TextBox(string b):text(b){};
        const char& fun1(const size_t& a)const  //表示TextBox对象为const,函数无法修改对象
        {
            cout<<text[a]<<"++1"<<endl;
            return text[a];
        }
        char& fun1(const size_t& a)  //不能仅按照返回值重载!!!
        {
            cout<<text[a]<<"++2"<<endl;
            return text[a];
        }
};
int main()
{
    
    const TextBox text("hello");
    text.fun1(1);  //走的是第一个fun1
	text.fun(1)不能当左值了,同时如果没有引用也不能当左值
	void fun2(const TextBox& a)
	{
		a.fun1() //为第一个
	}

特别注意的点是const成员函数可以用mutable摆动场来修饰需要更改的参数!下例子中,将text改成mutable,则可以在const函数中修改!

class TextBox
{
    private:
        mutable std::string text;

    public:
        TextBox(string b):text(b){};
        const char fun1(const size_t& a)const  //表示TextBox对象为const,函数无法修改对象
        {
            cout<<text[a]<<"++1"<<endl;
            text[a] = 'p';
            cout<<text[a]<<"++1"<<endl;
            return text[a];
        }
        char fun1(const size_t& a)  //不能仅按照返回值重载!!!
        {
            cout<<text[a]<<"++2"<<endl;
            return text[a];
        }
};

int main()
{
    
    const TextBox text("hello");
    text.fun1(1);

3.确定对象在使用前已经被初始化

Effective c++笔记----const_第4张图片

  1. 从内存的角度来讲,这里注意如果不用初始化列来初始化的话,对象会调用default构造函数,先赋初值,然后再调用拷贝赋值函数,如果是一个变量,例如numTimesCnsulted(0),则影响不大,但是为了一致性,都用初始化列表初始化class!
  2. 从正确性的角度来讲,编译器会为自建的对象自动调用default构造,而不会为内置类型设置初值,例如numTimesCnsulted(0),这样遗漏会出问题,同时const或者引用&也必须赋初值而不能等于赋值
    Effective c++笔记----const_第5张图片

Effective c++笔记----const_第6张图片

class Mesion
{
    public:
        Mesion(){cout<<"mesion"<<endl;}// 1
        Mesion(const Mesion& xiaomeison){cout<<"Copy mesion"<<endl;} //2
        Mesion& operator=(const Mesion& xiaomeison){cout<<"= mesion"<<endl;}  //3
        
};

class Jiangx
{
    public:
        Mesion mesion;

        Jiangx(Mesion& m):mesion(m){cout<<"Jiangx"<<endl;}
};

class JiangGZ
{
    public:
        Mesion mesion;

        JiangGZ(Mesion& m){mesion = m;
            cout<<"JiangGZ"<<endl;}
};
int main()
{
    Mesion ms;
    Jiangx jx(ms);  //2解决内存
    JiangGZ jgz(ms); //1,3

3.1 初始化顺序

A: 类的成员变量的初始化顺序只与变量在类中的声明顺序有关,与在构造函数中的初始化列表顺序无关
B: 静态成员变量先于实例变量,父类成员变量先于子类成员变量,父类构造函数先于子类构造函数。
以下为初始化顺序:

  1. 基类的静态变量或全局变量。
  2. 派生类的静态变量或全局变量。
  3. 基类的成员变量。
  4. 派生类的成员变量。
#include

using namespace std;

class A {
public:
    //我们原意是先初始化b,再用b初始化a。
    //但结果可以看出,类的成员变量的初始化顺序与声明顺序有关,先初始化的a,再初始化的b。
    A(int val):b(val),a(b){}
public:
    int a;
    int b;
};

int main(int argc, char* argv[]) {
    A A1(10);
    cout << A1.a << endl;
    cout << A1.b << endl;
    getchar();
    return 0;
}

3.2 extern使用回顾

4. 使用const T& 取代 pass by value

条款20
Effective c++笔记----const_第7张图片

你可能感兴趣的:(c++学习,effective,c++)