改善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的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...
随机推荐
- Tomcat详解系列(1) - 如何设计一个简单的web容器
Tomcat - 如何设计一个简单的web容器 在学习Tomcat前,很多人先入为主的对它的认知是巨复杂的:所以第一步,在学习它之前,要打破这种观念,我们通过学习如何设计一个最基本的web容器来看它需 ...
- polay计数原理
公式: Burnside引理: 1/|G|*(C(π1)+C(π2)+C(π3)+.....+C(πn)): C(π):指不同置换下的等价类数.例如π=(123)(3)(45)(6)(7),X={1, ...
- A New Stone Game POJ - 1740
题目链接:https://vjudge.net/problem/POJ-1740#author=0 题意:有n堆石子,每次你可以选一堆拿走任意数量的石子,而且你还可以选择从这一堆剩下石子中取任意数量石 ...
- ApiTesting全链路接口自动化测试框架 - 实战应用
场景一.添加公共配置 我们在做自动化开始的时候,一般有很多公共的环境配置,比如host.token.user等等,如果这些放在用例中,一旦修改,将非常的不便.麻烦(尤其切换环境). 所以这里我们提供了 ...
- SFDC 利用Schema.Describe来取得Picklist所有的选项
Salesforce的开发语言Apex与Java极为类似.也有封装,基础,多态特性. 并且也能 反射,Object的属性和Field属性. 今天主要记录的是一个需求:Visualforce Page或 ...
- QT项目-Chart Themes Example学习(一)
1.main.cpp #include "themewidget.h" #include <QtWidgets/QApplication> #include <Q ...
- Java中的映射Map - 入门篇
前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的映射Map - 入门篇>,希望对大家有帮助,谢谢 简介 前面介绍了集合List,这里开始简单介绍下映射Map,相关类如下图所示 正 ...
- 第15 章 : 深入解析 Linux 容器
深入解析 Linux 容器 今天的内容主要分成以下三个部分 资源隔离和限制: 容器镜像的构成: 容器引擎的构成: 前两个部分就是资源隔离和限制还有容器镜像的构成,第三部分会以一个业界比较成熟的容器引擎 ...
- 随便聊聊 Java 8 的函数式编程
函数式编程(Functional Programming) 首先,我们来了解一个叫做"编程范式"的概念. 什么是"编程范式"呢?简单来说就是指导我们编程的方法论 ...
- C语言利用for循环打印菱形
C语言利用for循环打印菱形(高度为奇数) 这次用的方法是上下部分分开打印,先打印上部分,再打印下部分. 先举个简单的例子打印,再改进代码,登堂入室从而理解. 例:打印一个高度(高度必须为奇数)为 5 ...