栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Effective C++笔记

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Effective C++笔记

视C++为一个语言联邦

C++是一个多重泛型编程语言,我们可以认为其由4个子语言组成:

  • C: C++刚开始时是C with class
  • Object-Oriented C++: C++面向对象的特性部分。如封装、继承、多态
  • Template C++: 泛型编模板元编程
  • STL:算法、容器、迭代器、函数对象等
用const、enum、inline替代#define
  • 对于常量,应该以const对象或者enum来代替#define
  • 对于宏函数,最好使用inline来替代#define
const和enum

#define是预处理,并不被当作语言的一部分,而是直接做文本替换,可能会带来意料外的错误。
如 #define printf cout会改变函数定义

形如

#define PI = 3.1415926
const double Pi = 3.1415926;

Pi会进入编译器的符号表,从而方便后续操作。

常量指针
定义常量指针,一般都是放在头文件的,因此最好声明为

const char* const name = "Tom";
// 更适用于string
const std::string name("Tom");

类中静态常量

struct A{
	static const int Number = 0;
	// 使用该常量
	int scores[Number];
};

一般编译器都支持默认类型可以在类内声明时赋值,只要不对其进行取地址操作,就可以直接使用. 否则的话需要单独定义.

struct A{
	static const int Number = 0;
	// 使用该常量
	int scores[Number];
};

/// in xx.cc文件
// 声明时赋过初值了.不可以再次赋值,否则会报错
const int A::Number;

enum hack
可以使用enum类型来定义常量,但是其特点是:

  • 不可以取地址. 所以通过enum hack,不可以获得指向某个整数常量的指针.

用来解决某些编译器不允许类内初始化的问题

struct A{
	enum {Number = 6;}
	int score[Number];
};
define与inline

一个例子.define可能会造成意外结果

#define MAX(a, b) (a) > (b) ? (a) : (b);

template
inline T findMax(const T &a, const T &b) {
    return a > b ? a : b;
}
int main() {
    int a = 1;
    int b = 1;
    int c = MAX(++a, b);
    cout << a << " " << b << " " << c << endl;
    a = 1, b = 1;
    c = findMax(++a, b);
    cout << a << " " << b << " " << c << endl;
}

使用宏函数的例子中,++a被执行了两次

尽可能使用const
  • 将某些需要的类型声明为const可以帮助编译器检查错误用法。 const可以修饰作用域内的任意成员函数、对象类型、函数参数、返回类型。
  • 编译器强制实行bitwise constness(不能在const函数内改变任何对象的指),但是不保证logical constness(可以修改指针指向的非对象内的指)。
  • 当const重载的函数内容一致时,可以通过非const函数调用const版来精简代码。
const与stl迭代器

迭代器是以指针为根据塑造出来的,其作用类似于一个T*的指针,因此直接作用于stl迭代器的const,类似于一个const指针,可以改变值但是不能改变指向. 想要一个指向const的迭代器,应该是const_iterator

int main() {
    vector vec{1, 2, 3, 4};

    const auto it1 = vec.begin();
    (*it1) = 0;
//    it1++;
    vector::const_iterator it = vec.begin();
//    (*it) = 1;
    it++;
}
函数返回const–减少意外赋值错误

可以减少if(a + b = c)这样将==意外打成=的错误
其中a b是重载的类型

const Type operator*(const Type& a, const Type& b){
	// ...
	return a;
}

这样就无法对其返回类型赋值,减少意外操作.

const成员函数
  • 使成员函数可以作用于const对象
  • 使接口便于理解

const可以重载成员函数

struct A {
    string s;
	
    A(string v) : s(v) {}

	// 返回类型必须是引用,因为内置返回类型无法被修改
    char &operator[](int i) {
        return s[i];
    }

    const char &operator[](int i) const {
        return s[i];
    }
};

int main() {
    A a("hello");
    a[0] = 'H'; // 调用非const函数
    cout << a.s << endl;

    const A b("hello"); // 调用const函数
    cout << b[0];
}

指针绕过const
const成员函数不可以修改对象内的任何non-static内容,但是如果const函数内,我们只修改了一个指针指向的值,而只有指针属于这个对象,其所指内容不属于对象,那么就可以通过const检查.但是不符合bitwise constness概念.如:

struct A {
    char *s;
	static int a;
    A(char *v) : s(v) {}

    void func() const {
        s[1] = 'L';
        a++; // 合法,是静态成员
    }
};
int A::a = 0;
int main() {
    char *c = new char[10];
    memset(c, '', 10);
    strcpy(c, "AAAA");
    A a(c);
    a.func();
    cout << a.s;
    // ALAA
}

mutable关键字可以使non-static对象改变

struct A {
    char *s;
    mutable int len;
    int getL() const{
        len = strlen(s);
        return len;
    }
};
在const和非const之间避免重复代码

利用const函数构造其非const的孪生版本
如上文,通过const可以重载函数,有时候两个函数之间代码完全一致,但是通过const重载会造成代码冗余,进而导致编译时间、维护难度等上升。

struct A {
    string s;
	
    A(string v) : s(v) {}

	// 返回类型必须是引用,因为内置返回类型无法被修改
    char &operator[](int i) {
    	// 可能的边界检查代码
    	// 可能的数据访问代码
    	// 可能的数据完整性校验
        return s[i];
    }

    const char &operator[](int i) const {
    	// 可能的边界检查代码
    	// 可能的数据访问代码
    	// 可能的数据完整性校验
        return s[i];
    }
};

我们可以通过const_cast和static_cast来完成代码复用。

struct A {
    string text;

    A(string s) : text(s) {}

    const char &operator[](int i) const {
        // 可能的边界检查代码
    	// 可能的数据访问代码
    	// 可能的数据完整性校验
        return text[i];
    }

    char &operator[](int i) {
        return const_cast(static_cast(*this)[i]);
    }

};

如上,我们通过static_cast(*this)将原类型转化为const类型,然后调用[],就会访问const函数。但是其返回值是const char&, 所以我们通过const_cast将其移除const属性。

注意:在非const函数内调用const函数是安全的,在const函数内调用非const函数是不安全的!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/917195.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号