改善c++程序的150个建议(读后总结)-------12-18
12.优先使用前置操作符
#include <iostream>
using namespace std;
class A
{
private:
int num;
public:
A operator++(); //前置++
A operator++(int); //后置++
};
A A::operator++()
{
(*this).num=(*this).num+1; //无须构造一个临时对象
return (*this);
}
A A::operator++(int)
{
A p1; //得先构造一个临时对象;
p1.num=(*this).num;
(*this).num=(*this).num+1;
return p1;
}
因为后置++的实现需要构造一个临时对象,当A类型对象(即被操作对象)很大时,其将会花费较长时间去构造对象,所以能用前置++解决的问题不用后置++解决。
**13.掌握变量定义的位置与时机 **
变量的使用规则是 :“当你需要时在定义”,在能满足需要的情况下(因为有时候不得不定义一些作用域较大的变量),尽可能的降低变量作用域的大小。
如果把所有的变量都在程序开头进行定义的话,当别人阅读你的代码时,需要不停地往前去看你这个变量是什么含义(就算是自己很长时间后再来阅读也会这样麻烦),所以在需要使用这个变量时在定义可以较快的使他人阅读自己的代码,增加了代码的可读性。
**14.小心typedef使用中的陷阱 **
不要认为typedef与宏定义等同,二者是有区别的。
#include <iostream>
#define p1 int*
using namespace std;
typedef int* p2
int main()
{
p1 a,b;
p2 x,y;
int c;
a=&c;
b=&c;
x=&c;
y=&c;
cout<<a<<endl<<b<<endl<<x<<endl<<y;
return 0;
}
结果会报错,显示“b=&c”有错,int*类型的值不能赋值给int类型的实体。
也就是b在定义时并没有定义为int *类型的而是定义成立int类型的,这是因为宏定义只是简单地符号替换,而typedef则不同,其可以有int *定义变量时一样的效果。
15. 尽量不要使用可变参数
int nasa(int a,…)其为含有可变参数函数的定义形式。
c语言中的printf(),和scanf()都是含有可变参数的函数,其在使用时可以支持任意数量,任意类型的参数。
虽然这样看似十分方便灵活但是其有很大的缺点。
(1)其十分不安全,其不会进行类型安全检查。
printf("%d,%c".256,256)
因为其printf的第二个参数应该接收字符类型,而其传的是int类型
其会对int类型的数据进行强制类型转换为字符类型,因为字符类型的长度小于int类型的,所以其发生内存截断(如果截断后的数据正好在字符类型数据范围内则不会报错,程序会发生意想不到的错误)。
(2)可变参数不支持自定义数据类型
例如printf函数只能支持基本数据类型,这也是为什么c++用输入输出流对象来进行基本的输入输出的原因之一,cout与cin就支持自定义数据类型(通过运算符重载),而且其使用方便,很随意不需要再像使用printf那样进行格式化输出,而且还极易出错。
c++有自己的方法实现类似于可变参数的这种功能,虽然没有其灵活但是其很安全。(其实就是通过函数重载来实现的)
class A
{
private:
public:
void nasa(int);
void nasa(char);
void nasa(int,char);
void nasa(char,int);
......
};
尽量不要使用可变参数,因为其真的很不安全,而且极易出错
16.不要用goto语句(不要用就对了)
17. 小心隐式转换带来的麻烦
显示强制类型转换虽有时候也会带来一定的麻烦但其是人为可控的,容易找到。但是隐式类型转换是编译器自动进行的,是很难察觉到的。
(1)基本类型的隐式转换
基本类型一般在运算时都是向更宽的类型转换。
(2)T到void的隐式转换
c语言支持void与其他类型的指针之间进行双向转换,而c++只支持其他类型的指针转换为void
(即任何类型的指针都可以赋值给void*类型,从而变为不指向任何类型的指针)
(3)子类到父类的隐式转换
即子类对象可以对父类对象进行赋值
(但父类不能隐式转换为子类,即父类对象不能对子类对象进行赋值)
(4)重载类型转换符与转换构造函数中涉及的隐式转换
重载类型转换符引起的隐式转换
class A
{
public:
int x;
int y;
public:
A(int a,int b):x(a),y(b){}
operator double()
{
return double(x)/double(y)
}
};
int main()
{
A P1(1,2);
cout<<p1;
return 0;
}
结果输出0.5,没有重载<<运算符为什么可以输出自定义类型呢?
其实因为当编译器没有找到合适的operator<<函数时,其会看能不能把带输出对象转换成能够执行operator<<函数的类型,正好重载的double类型转换符可以把其转换成double类型,所以待操作对象其被自己重载的double函数给隐式转换成double类型
1.0/2.0等于0.5后再调用operator<<函数进行输出
所以当重载了类型转换符的类对象如果没有对输出流对象进行重载的话,不能进行输出(否则会产生意料不到的麻烦)。
转换构造函数引起的隐式转换
(也包括含有一个参数的构造函数)
class A
{
public:
A(int a):num1(a){}
A(int a1,char a2='@'):num1(a2),num2(a1){}
void output();
private:
int num1;
char num2;
};
int main()
{
int w=2;
char ww='a';
A p1(w);
p1.output();
p1=ww;
p1.output();
return 0;
}
void A::output()
{
cout<<num1<<" "<<num2<<endl;
}
结果为:
2
0 a
原因是当p1=ww(一个char类型的对象值理论上是不能赋给一个类类型的对象的,其之所以可以赋值是因为编译器利用此类所含的构造函数A(int a1,char a2=’@’ ):num1(a1),num2(a2)对其进行了隐式转换成此类类型。)
为了解决这个问题应该在含有单参数的构造函数前加上explicit关键字
explicit A(int a):num1(a){}
explicit A(int a1,char a2='@'):num1(a2),num2(a1){}
这时候p1=ww语句就是被认为是不合法的,无法通过编译。
总结(4):由重载类型转换符带来的隐式转换问题,要注意是否重载了operator<<函数,否则不要试图输出本类对象。
由转换构造函数(包含单参数的构造函数)带来的隐式转换问题,注意在其构造函数定义前加上explicit关键字
18. 正确区分void与void *
(1)void是无类型的意思
①函数无返回值时应该将其声明为void类型
void nasa(参数表列)
②函数无参数时应该声明void
返回值类型 nasa(void)
实际可以写可以不写,但是有的编译器在没有void的情况下会报错,为了代码的可移植性,在编程中尽量加上void。
(2)void *(意思是指针不确定其指向什么类型)
不同类型的指针是不能直接相互转换的
int *p1;
char *p2
p1=p2
这是不允许的,其必须通过强制类型转换
p1=(int *)p2 这是合法的
而void *不一样,任何类型的指针都可以转换成void *
void *p1;
int *p2;
p1=p2;
这是允许的,c语言中还允许void *类型直接转换成其他指针类型,但是在c++中就不允许这么干。但是可以强制类型转换
p2=(int *)p1
一般类型的指针支持加上或减去一个数,
int *p;
int a=*(p+2)
p指针加上2表示p指针所指空间再向下偏移两个字节
但是void *类型的不行,因为其无法确定其指向的是什么类型,即无法确定其所指向空间的大小,因此不能进行加减一个数。
改善c++程序的150个建议(读后总结)-------12-18的更多相关文章
- 编写高质量代码_改善C++程序的150个建议 读书笔记
这几天看了下这本书<编写高质量代码_改善C++程序的150个建议>,觉的蛮有收获的,再次记录下自己以前不清晰的知识点,以供学习. 编写符合标准的main函数 C语言标准规定了main函数的 ...
- 改善c++程序的150个建议(读后总结)-------19-26
19. 明白在c++中如何使用c c++可以兼容c的绝大部分代码,但是还是有一部分不能兼容. c语言的编译器在调用函数时会把函数翻译成 : "_函数名",例如: int nasa( ...
- 改善c++程序的150个建议(读后总结)-------10-11
10. 优化结构体中元素的布局 结构体变量所占空间大小并不是其所含类型所占字节数之和,其所占内存字节数涉及到字节对齐. 字节对齐 :变量在内存中储存都是以字节数为单位,每一个字节都有自己的地址,逻辑上 ...
- 改善c++程序的150个建议(读后总结)-------0-9
0. 不要让main 函数返回 void 入口函数main()返回类型应该为 int, 即程序结束时return 0 表示程序正常返回,函数结束时 return -1 值表示程序异常返回, 如果不显式 ...
- 改善c++程序的150个建议(读后总结)-------27-35
27. 区分内存分配的方式 c++中内存分为5个不同的区 ①栈区 栈是一种特殊的数据结构,其存取数据特点为(先进后出,后进先出).栈区中主要用于存储一些函数的入口地址,函数调用时的实参值以及局部变量. ...
- 改善C++ 程序的150个建议学习之建议7:时刻提防内存溢出
作为一个程序员,对内存溢出问题肯定不陌生,它已经是软件开发历史上存在了近40年的大难题.在内存空间中,当要表示的数据超出了计算机为该数据分配的空 间范围时,就产生了溢出,而溢出的多余数据则可以作为指令 ...
- 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法
建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...
- 改善java程序的151个建议
<编写高质量代码-改善java程序的151个建议> --秦小波 第一章.开发中通用的方法和准则 1.不要在常量和变量中出现易混淆的字母 long a=0l; --> long a=0 ...
- 编写高质量代码改善java程序的151个建议——导航开篇
2014-05-16 09:08 by Jeff Li 前言 系列文章:[传送门] 下个星期度过这几天的奋战,会抓紧java的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...
随机推荐
- javascript 最权威的知识点总结
JavaScript中如何检测一个变量是一个String类型?请写出函数实现typeof(obj) === "string"typeof obj === "string& ...
- 鸿蒙运行报错:Failure[INSTALL_PARSE_FAILED_USESDK_ERROR] Error while Deploying HAP
问题描述 近期,使用DevEco-Studio新建手机类型的工程,编译成功,发布到模拟器(鸿蒙P40)时出错,如下图: 原因分析 本地DevEco-Studio使用的SDK版本与设备(P40)不匹配导 ...
- vue中的.sync修饰符用法
在项目中接触到父组件传值给子组件的时候,想在子组件改变父组件传的值.(比如用于弹窗关闭) 但是正常来说,vue2是不允许子组件直接改父组件传进去的值的. 所以我们需要在子组件内定义自定义事件,通知父组 ...
- C# .NET Socket 简单实用框架,socket组件封装
参考资料 https://www.cnblogs.com/coldairarrow/p/7501645.html 根据.NET Socket 简单实用框架进行了改造,这个代码对socket通信封装还是 ...
- 第30 章 : 理解 RuntimeClass 与使用多容器运行时
理解 RuntimeClass 与使用多容器运行时 本文将主要分享以下三方面的内容: RuntimeClass 需求来源 RuntimeClass 功能介绍 多容器运行时示例 RuntimeClass ...
- 第28 章 : 理解容器运行时接口 CRI
理解容器运行时接口 CRI CRI 是 Kubernetes 体系中跟容器打交道的一个非常重要的部分.本文将主要分享以下三方面的内容: CRI 介绍 CRI 实现 相关工具 CRI 介绍 在 CRI ...
- servlet学习(一)
Tomcat 注:以下资料摘自孙鑫的<sevlet/JSP深入详解>,仅用于个人学习使用. 一.web技术的发展 早期web是静态页面的浏览,使用HTML编写,放入服务器. 1.1浏览器请 ...
- vue-cli2 项目中使用node-sass
公司的项目,换了个电脑要重新安装一下依赖,但是直接npm install的时候报错了,提示node-sass未安装成功. 然后直接npn install node-sass --save 的时候还是下 ...
- [Fundamental of Power Electronics]-PART I-4.开关实现-4.2 功率半导体器件概述
4.2 功率半导体器件概述 功率半导体设计中最根本的挑战是获得高击穿电压,同时保持低正向压降和导通电阻.一个密切相关的问题是高压低导通电阻器件的开关时间更长.击穿电压,导通电阻和开关时间之间的折衷是各 ...
- JavaScript 中对象解构时指定默认值
待解构字段为原始值 正常情况下, const obj = { a: 1, b: 2, }; const { a, b } = obj; console.log(a, b); // 1 2 当被解构字段 ...