EffectiveC++1

2021/6/15 1:22:47

本文主要是介绍EffectiveC++1,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

C++为一个语言联邦

一开始C++只是C加上一些面向对象特性,但是随着这个语言的成熟他变得更加无拘无束,接受不同于C with classes的各种观念、特性和编程战略。异常对函数的结构化带来了不同的做法,templates将我们带来到新的设计思考方式,STL则定义了一个前所未见的伸展性做法。

今天C++已经是个多重范型编程语言,一个同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式的语言。这些能力和弹性使C++成为一个无可匹敌的工具,因此、将C++视为一个语言联邦。

尽量以cosnt、enum、inline替换#define

因为、宏定义会被预处理器处理,编译器并未看到宏定义的信息,当出现一个编译错误信息的时候,可能会带来困惑。

解决之道就是使用一个常量替换宏定义(#define)

const double AspectRatio = 1.653;   // 大写名称通常代表宏定义,因此这里可以使用首字母大写的方法表示const全局变量

作为一个语言常量,AspectRatio肯定会被编译器看到,当然就会进入符号表内。另外、使用常量也可以有较小的码、因为使用预处理会导致预处理器盲目的将宏名称替换为对应的数值,可能会导致目标码出现多份宏定义的数值。

基于数个理由enum hack值得我们认识。

class GamePlayer{
  private:
    enum {NumTurns = 5}; // enum hack 令NumTurns成为5的一个标记
    int scores[NumTurns]; //
};
  • enum hack的行为某方面来说比较像#define而不像const,有的时候这正是你想要的,例如取一个const的地址是合法的,但是取一个enum的地址就是不合法的,而取一个#define的地址通常也不合法。如果你不想让别人获得一个pointer或者reference指向你的某个整数常量,enum可以帮助你实现这个约束。
  • 虽然优秀的编译器不会为const对象设置存储空间,但是不够优秀的编译器可能会设置另外的储存空间,enum#define一样绝对不会导致非必要的内存分配。
  • 出于实用主义考虑,很多代码特别是模板元编程中用到了它,因此、看到它你必须认识他。

对于单纯的常量,最好以const对象或者enums替换#define

对于形似函数的宏(macros),最好改用inline函数替换#define

尽可能使用const

const的一件奇妙的事情是,它允许你指定一个语义约束,而编译器会强制实施这项约束。它允许你告诉拜你一起和其他程序员某值应该保持不变。

char greeting[] = "Hello"; 
char *p = greeting;    // non-const pointer, non-const data
const char* p = greeting;  // non-const pointer, const data
char* const p = greeting;  // const pointer non-const data
const char* const p = greeting; // const pointer, const data 

const语法虽然变化多端,但并不是莫测高深,如果关键字const出现在型号的左边,表示被指物是常量,如果出现在星号的右边,表示指针自身是常量,如果出现在星号两边,表示被指物和指针两者都是常量。

如果被指物是常量,有些程序员会将关键字const写在类型之前,有些人会把它写在类型之后、星号之前,这两种写法的意义相同,所以下列两个函数的参数类型是一样的:

void f(const Widget* pw);   // 一个指向常量的指针
void f2(Widget const* pw); // 一个指向常量的指针

两种形式都有人使用,是否是指向常量的指针,要看const相对于星号的位置,星号左边为指向常量的指针,星号右边为常量指针。

const修饰函数返回值,可以降低编码出现的低级错误

class Rational {};
const Rational operator*(const Rational& lhs, const Rational& rhs);
Rational a, b, c;
if (a*b = c) // 其实是想做个比较,当operator*返回值声明为const的时候将会返回错误,也就防止了编码不小心带来的异常

const修饰成员函数

  • 可以通过const得知哪些函数可以改动对象内容,哪些函数不可以
  • 使得操作const对象成为可能

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

关于将变量初始化这件事,C++似乎总是反复无常。但是有一点是可以确定的是,读取没有初始化的值会导致不确定行为

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

什么时候empty class不再是个空类呢?当C++处理过之后,是的,如果你没有自己声明,并一起就会为它声明(编译器版本)一个copy构造函数、一个copy assignment操作符和一个析构函数。

因此、如果你声明了一个empty class如下:

class Empty{};

编译器处理之后就好像你写了如下的代码:

class Empty {
public:
    Empty() {}                               // default构造函数
    Empty(const Empty& rhs) {}               // copy构造函数
    ~Empty() {}                              //析枸函数
    Empty& operator=(const Empty& rhs) {}    // copy assignment 操作符
};

唯有当这些函数被需要(被调用),它们才会被编译器创建出来。

好了,我们知道编译器会常见这些函数,但这些函数做了什么?default构造函数和析构函数,主要是给编译器一个地方放置藏在幕后的代码,像是调用base classnon-static成员变量的构造函数和析构函数。需要注意的是编译器默认的析构函数是non-virtual的。

若不想使用编译器自动生成的函数,就明确拒绝

有时你不想让用户使用某个函数,不对函数进行声明就行了。但是这样做对copy构造函数和copy assignment操作符却不起作用,因为、如果你不进行声明,编译器会声明一个默认的出来。

这就把你逼到一个困境,如果你不想让用户使用copy构造函数和copy assignment函数,你既不能不声明也不能进行声明。这个问题的解决方案就是,将函数声明为私有的函数,这样你即可以阻止编译器创建它们,又因为是私有函数,使得别人不能调用。

但是这样做并不是绝对安全的,因为member函数和friend函数还是可以调用private函数的。除非你足够聪明不去定义它们,那么如果任何人不慎调用了任何一个函数,将会导致一个链接错误,将成员函数声明为私有,而又故意不去实现它们是如此的受欢迎。、

class HomeForSale {
public:
   ...
private:
    HomeForSale(const HomeForSale&);    // 因为根本没有人能调用,写参数名称也是浪费
    HomeForSale& operator=(const HomeForSale&);
};

有了上述的定义之后,当用户企图调用拷贝HomeForSale对象的时候,编译器会阻止他,如果不慎在member或者friend函数中调用,连接器也会发出抱怨。

为了驳回编译器自动提供的功能,可将相应的成员函数声明为private并且不予实现。



这篇关于EffectiveC++1的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程