C++primerplus

简介: C++primerplus

您可能想将一些用C或者老式C++版本开发的程序转换为标准C++,本附录提供了这方面的一些指南,其中的一些内容是关于从C转换为C++的,另一些是从老式C++准欢换为标准C++的
1.1使用一些预处理编译器编译指令的替代品
C/C++预处理器提供了一系列的编译指令,通常,C++惯例是使用这些编译指令来管理的编译过程,而避免用编译指令替换代码,例如,#include编译指令是管理程序文件的重要组件,其他编译指令如#ifndef和#endif使得能够控制是否对特定的代码块进行编译,#pragma编译指令能够控制编译器特定的编译选项,这些都是非常有帮助有时是必不可少的工具,但使用#define编译指令时应谨慎。
1.1.1 使用const而不是#define来定义常量
符号常量可提高代码的可读性和可维护性,常量名指出来了其含义,如果想要修改它的值,只需定义修改一次,然后从新编译即可,C使用预处理器来创建常量的符号名称。

define MAX_LENGTH 100

这样,预处理器将在编译之前对源代码执行文本置换,即用100替换所有的MAX_LENGTH.
而C++则在变量声明使用限定符const
const intMAX_LENGTH = 100;
这样MAX_LENGTH将视为一个只读的int变量
使用const 方法有很多优越性,首先,声明显式指明了类型,使用#define时,必需在数字后加上各种后缀来指出除char、int或者double之外的类型,例如,使用100L来表明long类型,使用3.14F来表明float类型,更重要的是,const方法可以很方便地用于复合类型,如下例所示,:
const int base_vals{5} = {100,2000,3500,6000,10000};
const string ans[3] = {"yes","no","maybe"};
最后,const标识符遵循变量的作用域规则,因此,可以创建作用域为全局、名称空间或数据块的常量,在特定函数中定义常量时,不必担心其定义会与程序的其他地方使用的全局变量冲突,例如,对于下面的代码:

define n 5

const int dz = 12;

void fizzle()
{
int n ;
int dz;

}
预处理器将把:
int n;
替换为:
int 5;
从而导致编译错误,而fizzle()中定义的dz是本地变量,另外,必要时,fizzle()可以使用作用域解析
操作符(::),以::dz的方式访问该常量。
虽然C++借鉴了C语言中的关键字const,但C++版本更有用,例如对于外部const
值,C++版本有内部链接,而不是变量和C中const所使用的默认外部链接,这意味着使用const
的程序中的每个文件都必需定义该const,这好像增加了工作量,但实际上它使工作更简单,使用内部链接时,可以将const定义放在工程中的各种文件使用的头文件中,对于外部链接,这将导致编译错误,但对于内部链接,情况并非如此,另外由于const必须在使用它的定义中定义,在该文件使用的头文件中,对于外部链接,这将导致编译错误,但对于内部链接,情况并非如此,另外由于const必须在使用它的定义(在该文件使用的头文件中定义也满足这样的需求)因此可以将const值用作数组长度参数
const int MAX_LENGTH = 100;
double loads[MAX_LENGTH];
for(int i =0;int<MAX_LENGTH;i++)
loads[i] = 50;
这在C中是行不通的,因为定义MAX_LENGTH的声明可能位于一个独立的文件中,在编译中,该文件可能不可用,坦白地说,在C中,可以使用static限定符来创建内部链接常量,这就是说,C++通过默认使用static,让您可以少记住一件事。
顺便说一句,修订后的C标准,C99,允许const用作数组长度,但必须将数组作为一种新式数组---变量数组,而这不是C++标准的一部分。
在控制何时编译头文件方面,#define编译指令仍然很有帮助:
//blooper.h

ifndef _BLOOPERH

define _BLOOPERH

//code goes here

endif

但对于符号常量,习惯上还是使用const,而不是#define,另一个好方法---尤其是在有一组相关的整型常量时,是使用enum;
enum{LEVEL} = 1.LEVEL2=2,LEVEL3=4,LEVEL4=8};
1.1.2使用inline而不是#define来定义小型函数
在创建类似于内联函数的东西时,传统的C语言方式是使用一个#define另一个好方法--尤其是在有一组相关的整型常量时----是使用enum;
enum[LEVEL] = 1.LEVEL2=2.LEVEL3=4.LEVEL4=8};
1.1.2使用inline而不是#define来定义小型函数
在创建类似于内联函数的东西时,传统的C语言是使用#define宏定义:

define Cube(X)XXX

这将导致预处理器进行文本置换,将X替换为Cube()的参数:
y=Cube(X);//replaced with y = xx
这将导致预处理器进行文本置换,将X替换为Cube()的参数
由于预处理器使用文本置换,而不是真正的传递参数,因此使用这种宏可能导致意外的错误的结果,要避免这种错误,可以在宏中使用大量的圆括号来确保正确的运算顺序。

define Cube (X) ((X)(X)*(X))

但即使这样做,也无法处理使用诸如Z++等值的情况
C++方法是使用关键字inline来标识内联函数,这种方法更可靠,因为它采用的是真正的参数传递,
另外,C++内联函数可以是常规函数,也可以是类方法
class dormant
{
private :
int peroid:

public :
int Peroid()const {
return peroid;
}//automatically inline

};

define宏的一个优点是,它是无类型的,因此将其用于任何类型,运算都是有意义的,在c++中,可以创建内联函数模板来使函数独立于类型,同时传递参数

简而言之,请使用C++内敛技术,而不是C语言中的#define宏
1.2使用函数原型
实际上,您没有选择的余地,虽然在C中,原型是可选的,,但是在C++中,它确实是必不可少的 ,请注意,在被使用之前定义的函数,如内联函数,是其原型
应尽可能在函数原型和函数头中使用const,具体的说,对于表示不可修改的数据的指针参数和引用参数,应使用const,这不仅使编译器能够捕获修改数据的错误,也使函数更为通用,也就是说,接受const指针或者引用函数能够同时处理const数据和非const数据,而不使用const指针或饮用的函数只能处理非const数据
1.3使用类型转换
stroustrap对c语言的抱怨之一是其无规律可循的类型转换操作符,,确实,类型转换通常是必需的,但标准类型转换太不严格,例如,对于下面的代码
struct Doof{
double feeb;
double steeb;
char sqif[10]
};
Doof leam;
short ps = (short )&leam//old syntax
int pi = int
(&leam);//new syntax
1.4逐渐熟悉C++特性
如果使用的是malloc()和free(),请改用new和delete;如果使用setjmp()和longjmp()处理错误,则请改用trythrow和catch.另外对于标识true和false的值,则将其类型声明为bool。
1.5使用新的头文件
c++标准指定了头文件的新名称,请参见第2章,如果使用的是老式头文件,则应当改用新名称。
这样做不仅仅是形式上的改变,因为新版本有时增强了特性,例如头文件ostream提供了对宽字符输入和输出的支持,还提供了新的操纵符,如boolalpha和fixed(请参见第17章)对于众多格式化选项的设置来说,这些操纵符符合提供的接口

1.6使用名称空间
名称空间有助于组织程序中使用的标识符,避免名称冲突,由于标准库是使用新的头文件组织实现的,它将名称放在std名称空间中,因此使用这些头文件需要处理名称空间
出于简化的目的,本书的范例都使用编译指令using 来使std 名称空间中的名称可用

include

include

include

using namespace std;//a using directive
不过,不管需要与否,都导出名称空间中的所有名称,是与名称空间的初衷背道而驰的。
稍微要好些的方法是,在函数中使用using编译指令,这将使名称在该函数中可用,
更好也是推荐的方法是,使用using声明或作用域解析操作符(::),只使程序需要的名称可用,例如,下面的代码:

include

using std:::cin; //a using declaration
using std::cout;
using std::endl;

使cin、cout和end1可用于文件的剩余部分,但使用作用域解析操作符只能使名称在使用该操作符的表达式中可用
cout<<std::fixed<<x<<endl;//using the Scope resolution operation

这样做可能很麻烦,但可以将通用的using 声明放在一个头文件中;
//mynames--a header file
using std::cin;
using std::cout;
using std:endl;
还可以将通用的using声明放在一个名称空间中
//mynames --a header file

include

目录
相关文章
|
9月前
|
Java 数据库连接 应用服务中间件
原生JDBC使用C3p0数据源和dbcp数据源
原生JDBC使用C3p0数据源和dbcp数据源
87 0
|
2月前
|
机器学习/深度学习 运维 算法
[WWW2024]轻量数据依赖的异常检测重训练方法LARA
阿里云计算平台大数据基础工程技术团队主导,与浙江大学合作的论文《LARA: ALight and Anti-overfitting Retraining Approach for Unsupervised Time Series Anomaly Detection 》被WWW2024收录
|
存储
数据结构——线性表的链式表示和实现(链表总览)
数据结构——线性表的链式表示和实现(链表总览)
305 0
数据结构——线性表的链式表示和实现(链表总览)
|
9月前
|
存储 前端开发 搜索推荐
自定义MVC框架【上篇】--原理
自定义MVC框架【上篇】--原理
70 0
|
9月前
|
消息中间件 存储 Kafka
超硬核,基于mmap和零拷贝实现高效的内存共享(上)
超硬核,基于mmap和零拷贝实现高效的内存共享
|
存储 算法 NoSQL
第4章 内存管理
第4章 内存管理
373 0
第4章 内存管理
|
机器学习/深度学习 自然语言处理 算法
ELMo、GPT、BERT、X-Transformer…你都掌握了吗?一文总结文本分类必备经典模型(三)
ELMo、GPT、BERT、X-Transformer…你都掌握了吗?一文总结文本分类必备经典模型(三)
324 0
|
存储 安全 小程序
哪一个权限控制模型呢?(DAC|MAC|RBAC|RuBAC|ABAC)
哪一个权限控制模型呢?(DAC|MAC|RBAC|RuBAC|ABAC) 前言 最近想给自己的小程序的GPT调用接口增加一个权限控制,比如可以是这类策略:普通用户智能免费调用10次该接口,然后用户可以购买VIP会员,然后就可以无限次调用该接口。 社区中听得最多的权限控制模型就是RBAC,比如Nest的官方文档中介绍权限控制的章节就是以该模型为例。
479 0
|
XML 前端开发 安全
Spring Boot面试题1
Spring Boot面试题
143 0