改善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的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...
随机推荐
- c/c++ switch case内括号
如果在case语句中有定义变量,则必须要加{},否则会报错.
- 攻防世界 reverse easy_Maze
easy_Maze 从题目可得知是简单的迷宫问题 int __cdecl main(int argc, const char **argv, const char **envp) { __int64 ...
- 攻防世界 reverse serial-150
serial-150 suctf-2016 直接使用ida发现main函数中夹杂大片数据,应该是自修改代码,动态调试: 调试中发现,输入为16位,验证方法为:从头开始取一字符进行比较,比较通过检验后, ...
- Java学习之路 -- Java怎么学?
@ 目录 java基础怎么学? 学完基础学什么? 几个常用框架学完学什么? MQ JVM的知识跑不掉 微服务等等 其他 数据结构和算法 java基础怎么学? 当时,作为懵懂的小白,大一学习了c和c++ ...
- OpenCV 之 平面单应性
上篇 OpenCV 之 图象几何变换 介绍了等距.相似和仿射变换,本篇侧重投影变换的平面单应性.OpenCV相关函数.应用实例等. 1 投影变换 1.1 平面单应性 投影变换 (Projectiv ...
- SpringCloud(六)分布式事务
在分布式系统中,分布式事务基本上是绕不开的, 分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上 .其实就可以简单理解成在分布式系统中实现事务 ...
- Kubernetes 常用日志收集方案
Kubernetes 常用日志收集方案 学习了 Kubernetes 集群中监控系统的搭建,除了对集群的监控报警之外,还有一项运维工作是非常重要的,那就是日志的收集. 介绍 应用程序和系统日志可以帮助 ...
- Object.assign()和解构赋值:给对象赋值的两种方法
一.Object.assign()方法给对象赋值 Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象.它将返回目标对象. 拷贝的是属性值 如果目标对象中的属性 ...
- 神奇的魔方阵--(MagicSquare)(2)
在上一篇博客中,我们讨论了阶数为奇数,以及阶数为(4K)的魔方阵的排列规则,以及代码实现(详见:https://www.cnblogs.com/1651472192-wz/p/14640903.htm ...
- Ambassador-07-熔断
Ambassador的熔断机制的定义 circuit_breakers: - priority: <string> max_connections: <integer> max ...