Effective C++ 1-3

1.视C++为一个语言联邦

c++并不是一个语言,它是由4个次语言组成,分别是C、Object-Oriented C++、Template C++和STL。(其实这4部分不仅可以体现c++的主要特性,也可以从4个方面学习c++,并可以从这4个方面验证自己对c++了解和掌握程度)。因此它的高效编程守则对于同一个对象也会有多种要求。所以c++高效编程守则要视状况而变化,因为这取决于你当前状况偏向于哪个次语言。
举个栗子:传参时候并不都是选择pass-by-reference就是高效,要看其偏向于哪个次语言:如果是内置类型,比如int这种自带的,因为其偏向于C语言,所以pass-by-value会更有效;如果是对象,其偏向于Object-Oriented C++,所以pass-by-reference更高效,所以要具体情况具体分析~

总结:选择哪种高效的原则要看当时状况偏向哪个次语言。

2. 尽量以const、enum、inline替换#define

1)由于#define定义的常量名未进入symbol table,eg:#define ARRAY_NUM 10 调试时只能看到数字10而不能看到名字ARRAY_NUM,所以导致调试时看到该数字会有点莫名其妙。

2)不能对#define或enum定义的变量名取地址,这个是跟他们存放的内存相关吧~

3)宏定义后,若在某处被#undef,其后该宏就会无效

4)编译器要在编译期间知道数组的大小,所以不能酱紫:

int nArrayLength = 10;
int array[nArrayLength];

但是可以酱紫:

const int nArrayLength = 10;
// 或者
enum{nArrayLength = 10};
int array[nArrayLength];

是因为const修饰后的变量存放位置就不同了?

c++中,一个const不是必需创建内存空间,而在c中,一个const总是需要一块内存空间。

在c++中是否要为const全局变量分配内存空间,取决于这个const变量的用途,如果是充当着一个值替换(即就是将一个变量名替换为一个值),那么就不分配内存空间,不过当对这个const全局变量取地址或者使用extern时,会分配内存,存储在只读数据段。也是不能修改的。

c++中对于局部的const变量要区别对待:

对于基础数据类型,也就是const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存
对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存
对于自定数据类型,比如类对象,那么也会分配内存。

c中const默认为外部连接,c++中const默认为内部连接.当c语言两个文件中都有const int a的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。如果想让c++中的const具有外部连接,必须显示声明为: extern const int a = 10。
————————————————
原文链接:https://blog.csdn.net/woainilixuhao/article/details/86521357

根据资料可知,因为const修饰后的变量nArrayLength存放在符号表,所以编译器在编译期间就知道了数组大小,所以编译通过。

5)相比于函数调用,为啥说宏不会带来额外开销?

因为额外开销指的是开辟栈空间存放参数和返回值。

首先,函数调用会带来额外的开销,它需要开辟一片栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆栈。这种开销不仅会降低代码效率,而且代码量也会大大增加,而使用宏定义则在代码规模和速度方面都比函数更胜一筹;其次,函数的参数必须被声明为一种特定的类型,所以它只能在类型合适的表达式上使用,我们如果要比较两个浮点型的大小,就不得不再写一个专门针对浮点型的比较函数。反之,上面的那个宏定义可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以用“>”操作符比较值大小的类型,也就是说,宏是与类型无关的。

6)虽然宏不会带来额外的开销,但是有时候容易使用不当,导致不能得到预期结果;并且宏不会检查参数类型。
eg:

#define CALL_MAX_VALUE(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_MAX_VALUE(++a, b);
// 此时a=7,因为传入参数时+1,返回时又+1了

但可以使用template inline函数替代宏:

template<typename T>
    inline T CallWithMax(const T& a, const T& b)
{
     return (a > b ? a : b);
}
从逻辑上来说,编译器对inline函数的处理步骤一般如下:
(1)将inline函数体复制到inline函数调用处;
(2)为所用inline函数中的局部变量分配内存空间;
(3)将inline函数的的输入参数和返回值映射到调用方法的局部变量空间中;
(4)如果inline函数有多个返回点,将其转变为inline函数代码块末尾的分支(使用GOTO)。

inline函数相对宏函数有如下优点:
(1)内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。
(2)内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。
————————————————
原文链接:https://blog.csdn.net/K346K346/article/details/52065524

可以将内联理解为C++中对于函数专有的宏,对于C的函数宏的一种改进。对于常量宏,C++提供const替代;而对于函数宏,C++提供的方案则是inline。并且,inline对声明没有作用,必须要在函数的定义时使用inline

总结

关于#define 变量名,由于”***#define 变量名 值***“的变量名是不会出现在符号表,不便于调试,所以用***const 类型 变量名 值***替代。

关于#define 函数,由于#define 函数不会检查传入参数的类型以及容易使用不当导致得到结果不合预期,所以用inline替代(在函数定义时使用inline修饰),只有短小代码段才用inline,不然也很浪费内存~

3. 尽可能用const

1)对指针使用const:

const int * p; // 由于const是修饰*p,所以这个const约束的是p指向的内容,即指针指向的内容

int * const p; // 由于const是修饰p,所以这个const约束的p,即指针的值

2)对迭代器使用const:(迭代器类似指针,但是又不同于指针,所以const对其修饰不能用上面的来理解)

const vector<int>::iterator iter;  // const修饰的是迭代器,所以迭代器不能变

vector<int>::const_iterator iter; // const修饰的是迭代器指向的内容,所以迭代器指向的内容不能变

3)const修饰返回值,使函数返回一个常量值,可以避免出现“想要键入==却键成=的错误”。

例子:

Rantional a,b,c;

if(a*b = c)
    ...

因为a、b不是内置类型,所以编译器不会报错,导致if并没有判断a*b的值是否等于c就直接进入这个条件语句了,导致逻辑出错。

4)const修饰成员函数:不可以改动对象内容,用于操作处理const对象。
另外,编译器对const成员函数是执行bitwise constness,即成员函数不能改变对象的任何non-static的成员变量,因为static变量并不属于该类了,他是属于大家的。
通过用mutable修饰变量可以释放const成员函数的约束。

5)两个成员函数如果只是常量性不同,那么可以被重载,eg:

class XX
{
int func() const; // 创建的XX对象是const的,那么就会调用这个func
int func(); // 创建的XX对象是非const的,那么就会调用这个func
}
方法重载是指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同.
重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。

6)用const_cast去掉const,用static_cast加上const,从而实现令non-const版本调用const版本可避免代码重复性。

eg:

const char& operator[](std::size_t position) const
{
    // 边界检验等其他操作
    ...
    return text[position];
}
char& operator[](std::size_t position)
{
    return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}

Q: 书本提到将“边界检验等其他操作”这部分代码移到另一个成员函数中,然后两个版本的operator[]调用它会重复一些代码,例如函数调用,两次return语句等。那为啥这种常量性移除调用[]就避免了代码重复?

总结

关于const指针, 记住const指针和const迭代器的理解是相反的;
关于const成员函数,当成员函数参数和返回值都一样,就一个有const,一个没有const,这也是一种重载;mutable修饰成员变量就可以去掉const成员函数的对该成员变量的约束,PS:const成员函数仅仅约束的是对成员变量的更改。
关于加减const:const_cast是去掉const,static_cast是加上cons。

插播:static_cast常被用作显式类型转换使用;const_cast修改类型的const或volatile属性。(volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。即volatile的变量是说这变量可能会被意想不到地改变,所以每次都要重新读值)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值