c++ 尽量不要使用#define 而是用const、enum、inline替换。

例如:这里程序文件开头有如下#define语句

代码如下:

#define N 10
#define PI 3.14
#define MAX 10000
#define Heigth 6.65
...
...

假设这里程序运行出错误,而且就是在我们使用这些常量有错误,此时编辑器应该会抛出错误信息。如果该信息提示6.65这里有错误,Ok如果你运气好你正好记得或者程序简单一眼能找到6.65表示什么,如果程序很复杂,而且报出6.65的文件是引用该文件,不记得,那么你会困惑这是什么?或许会花大量时间去追踪6.65是什么?进而定位问题。
为什么错误信息是6.65呢?而不是Heith呢?因为在预编译阶段,已经用5.65来代替Heigth,Height没有进入记号表(system table)内。解决之道是可以使用下面语句进行替换
const double treeHeight=5.68;
   作为一个语言常量,treeHeight肯定会被编译器获知,并且进入记号表内。报出的错误不在是数字而是变量名称,这样有利于定位问题。
这里特别说明一下常量替换#define有两种特殊情况。
   第一个是定义常量指针。这里要将指针定义为常量指针,同时该指针也是指向一个常量,所以是下面的形式:
const char * const HZ="Hang Zhou";
在C++中最好使用string对象来替换掉char*形式:
const std::string HZ ("Hang Zhou");
第二个值得注意的就是class专属常量。首先将作用于限制到类内,必须将其声明为其成员。其次确保此常量至多只有一份实体,必须让它称为static成员。例如:


代码如下:

class People
{
private:
static const int Number=10;
int phoneNumbers[Number];
......
}

这看到的是声明式,而非定义式。通常C++要求你对使用的任何东西提供一个定义式。 或者使用enum,对于形式函数的宏,尽可能用inline或者template来代替。但是如果它是个class专属常量又是static且为整数类型(int,char,bool)则需特殊处理。只要不娶它们地址,则只用声明而不用提供定义式子。但是如果取class专属常量地址,纵使不取其地址编译器就要你提供定义式子。
static const int People::Number
这里定义不设初始值,是因为声明的时候已经获取了初值。

这里可以使用enum完成类似的功能


代码如下:

class People
{
private:
enum { Number = 10 };
int phoneNumbers[Number];
....
}

enum比较像#define而不像const。因为取const的地址是合法的,取一个enum的地址就不合法,取#define地址通常就不合法。所以可以通过enum来实现不让他人取得某个常量的地址。

下面介绍一道笔试题目


代码如下:

#define PRODUCT(a,b) a*b
....
int a=5,b=3,c;
c=PRODUCT(a+3,b+4);

那么c的值为多少?c=5+3*3+4=18而不是程序员预想的56,如果想达到预期的结果必须这样写

#define PRODUCT(a,b) ((a)*(b))
或许这样你还会坚持会写宏函数,因为你想说只用写一个宏函数就能完成int,flaot,double等类型的乘积运算。那么在看看下面例子

#define MAX(a,b) ((a)>(b)?(a):(b))

int a=5,b=3

MAX(++a,b); //a被加了两次

MAX(++a,b+4); //a被加了一次
a被加的结果可能不是预期的,完全可以用template inline函数达到宏的预期效果,并且效率与宏差不多。


代码如下:

template<typename T>
inline void Max(const T& a,const T& b)
{
f(a>b?a:b);
}

inline函数是一种编译机制,有点从代码上是看不出来的,但是从程序的执行效率上有差别,通常编译器对函数调用的处理是一种类似中断的方式,即当执行到函数调用语句时,会将当前所有信息存储到寄存器中,在去执行函数的代码,执行完后取回寄存器值,恢复到调用函数开始的状态,继续执行代码。声明为 inline 函数后,编译器不把函数编译成调用函数而是将代码拷贝到被调用的地方。所以效率上要比普通函数高一些,少了存寄存器与取寄存器信息的步骤。
另外需要注意的是inline函数最好写到.h文件中去,或者直接写到类中去。
const允许程序员指定一个语义约束,即不能被改动,编译器会强制实施这项约束。它表示被它修饰的值是不变的。const可以在classes外部修饰global或namespace作用域中的常量,或修饰文件、函数或者static对象以及指针。在const应用在指针中要注意关键字const出现在“*”的什么地方,如果在左边表示被指向的值是常量,如果在右边表示指针自身是常量。出现两边表示两者的功能的并集。这里特别说以下几点:
(1)迭代器中的cosnt
const std::vector<int>::iterator iter=vec.begin(); //相当于iter不能改变

std::vector<int>::const_iterator citer=vec.begin(); //iter所指向的内容无法改变
(2)将函数返回值声明为常量,不仅可以降低因程序员错误造成的不可预料的情况,并且不用放弃安全性和高效性。例如:

const operater *(const &lhs,const &rhs);
if((a * b = c);//本意是if(a*b==c)因程序员马虎而写成这样
如果a和b都是内置类型,此代码就不合理,但是是我们自定义类型,就可能行得通,如果声明返回值是cosnt,就可以预防这么做了。

(3)const成员函数,它是为了确认成员函数可作用于const对象。而且两个成员函数如果只是常量性不同,是可以重载的。成员函数后面跟const,表示该函数不能更改类的成员变量(下面有代码验证,如果尝试对其成员赋值,编译器会爆出错误)。原理就是编译器将其认为是只读变量。并且大多数const对象用于引用传递或者指针传递。


代码如下:

#include <iostream>
#include <string>

class People
{
public:
People():m_sName(""),m_iAge(0){}
People(std::string name,int age):m_sName(name),m_iAge(age){}
void set(int age)
{
this->m_iAge=age;
}

void set2(int age) const
{
this->m_iAge=age;
}

int get()
{
return this->m_iAge;
}
private:
std::string m_sName;
int m_iAge;
};

int main(int argc,char **argv)
{
People* p=new People("sky",8);
p->set(10);
std::cout<<p->get()<<std::endl;
p->set2(12);
std::cout<<p->get()<<std::endl;
delete p;
return 0;
}

编译该文件会报以下错误信息
const_test.cpp: In member function `void People::set2(int) const':
const_test.cpp:16: error: assignment of data-member `People::m_iAge' in read-only structure
const_test.cpp:36:2: warning: no newline at end of file
cosnt重载(注意:仅当形参是引用或者指针时候,形参是否为const才会有影响)。可以试一试我们将下面代码的&去掉,传入const_int其实调用的是void set(int age)函数,说明形参的const是没有起作用的。下面是验证代码


代码如下:

#include <iostream>
#include <string>
class People
{
public:
People():m_sName(""),m_iAge(0){}
People(std::string name,int age):m_sName(name),m_iAge(age){}
void set(const int& age) const
{
std::cout<<"this is const"<<std::endl;
}

void test(int& age)
{
std::cout<<"this is non-const"<<std::endl;
}

void test(short age)
{
std::cout<<"this is non-const"<<std::endl;
}

int get()
{
return this->m_iAge;
}
private:
std::string m_sName;
int m_iAge;
};

int main(int argc,char **argv)
{
People* p=new People("sky",8);
const int const_int=12;
p->test(const_int);
std::cout<<p->get()<<std::endl;
delete p;
}

(4)关于重载函数代码重复的问题。由经验可以得出我们通过const重载的函数往往有大量的代码是重复,甚至是一样的。如果大部分重复代码,我们可以将这些重复的代码写成一个函数,在由它们分别调用。如果是一样的,如下代码,我们就可以在non-const函数中调用const函数,来解决代码重复。


代码如下:

class People
{
public:
People():m_sName(""),m_iAge(0){}
People(std::string name,int age):m_sName(name),m_iAge(age){}
void eat(const People & Person) const
{
std::cout<<"this person info is:{age ="<<Person.m_iAge()<<",name ="<<Person.m_sName()<<std::endl;
std::cout<<"eating"<<std::endl;
std::cout<<"end"<<std::endl;
}

void eat ( People & Person)
{
std::cout<<"this person info is:{age ="<<Person.m_iAge()<<",name ="<<Person.m_sName()<<std::endl;
std::cout<<"eating"<<std::endl;
std::cout<<"end"<<std::endl;
}
private:
std::string m_sName;
int m_iAge;
};

然后在non-const eat函数中首先将*this类型由People&显示的转化为const People&让其调用const函数,即函数重载


代码如下:

#include <iostream>
#include <string>
class People
{
public:
People():m_sName(""),m_iAge(0){}
People(std::string name,int age):m_sName(name),m_iAge(age){}
void eat(const People & Person) const
{
std::cout<<"this person info is:{age ="<<Person.m_iAge<<",name ="<<Person.m_sName<<"}"<<std::endl;
std::cout<<"eating"<<std::endl;
std::cout<<"end"<<std::endl;
}

void eat(People & Person)
{
static_cast<const People&>(*this).eat(Person);
}
private:
std::string m_sName;
int m_iAge;
};

int main(int argc,char **argv)
{
People Person("sky",8);
Person.eat(Person);
}

运行的结果为

this person info is:{age =8,name =sky
eating
end

(0)

相关推荐

  • c++内联函数(inline)使用详解

    介绍内联函数之前,有必要介绍一下预处理宏.内联函数的功能和预处理宏的功能相似.相信大家都用过预处理宏,我们会经常定义一些宏,如 复制代码 代码如下: #define TABLE_COMP(x) ((x)>0?(x):0) 就定义了一个宏. 为什么要使用宏呢?因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方.这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行.因此,函数调

  • c++中inline的用法分析

    在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联. inline int min(int first, int secend) {/****/};inline函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数.与非inline函数不同的是,inline函数必须在调用该函数的每个文本文件中定义.当然,对于同一程序的不同文件,如果inline函数出现的话,其定义必须相同.对于由两个文件compute.C和draw.C构成的程序来说,程序员不能定义这样的min(

  • C++中inline函数详解

    本文主要记录了C++中的inline函数,也就是内联函数,主要记录了以下几个问题: 一.C++为什么引入inline函数? 主要目的:用它代替C语言中表达式形式的宏定义来解决程序中函数调用的效率问题. C语言中的宏定义:#define ExpressionName(var1,var2) (var1+var2)*(var1-var2)这种宏定义,它使用预处理器实现,没有了参数压栈.代码生成等一系列得到操作,因此效率很高.但缺点如下: 仅仅是做预处理器符号表中的简单替换,因此不能进行参数有效性的检测

  • C++ 关键字 inline详细介绍

    1.  内联函数 在C++中我们通常定义以下函数来求两个整数的最大值: 复制代码 代码如下: int max(int a, int b){ return a > b ? a : b;} 为这么一个小的操作定义一个函数的好处有: ① 阅读和理解函数 max 的调用,要比读一条等价的条件表达式并解释它的含义要容易得多 ② 如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多 ③ 使用函数可以确保统一的行为,每个测试都保证以相同的方式实现 ④ 函数可以重用,不必为其他应用程序重写代码 虽

  • 详解C++中的inline用法

    1. 引入inline关键字的原因 在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数. 栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间. 在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭. 下面我们来看一个例子: #include <stdio.h> //函数定义为inline即:内联函数 inline char*

  • C++实现inline hook的原理及应用实例

    本文实例简述了C++实现inline hook的原理及应用,对于大家更好的理解inline hook原理及其应用有很大的帮助.具体内容如下: 一.Inline Hook简介: 1.INLINE HOOK原理: Inline Hook通过硬编码的方式向内核API的内存空间(通常是开始的一段字节,且一般在第一个call之前,这么做是为了防止堆栈混乱)写入跳转语句,这样,该API只要被调用,程序就会跳转到我们的函数中来,我们在自己写的函数里需要完成3个任务: 1)重新调整当前堆栈.程序流程在刚刚跳转的

  • C/C++中static,const,inline三种关键字详细总结

    一.关于staticstatic 是C++中很常用的修饰符,它被用来控制变量的存储方式和可见性,下面我将从 static 修饰符的产生原因.作用谈起,全面分析static 修饰符的实质. static 的两大作用: 一.控制存储方式 static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间. 引出原因:函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保

  • c++ 尽量不要使用#define 而是用const、enum、inline替换。

    例如:这里程序文件开头有如下#define语句 复制代码 代码如下: #define N 10 #define PI 3.14 #define MAX 10000 #define Heigth 6.65 ... ... 假设这里程序运行出错误,而且就是在我们使用这些常量有错误,此时编辑器应该会抛出错误信息.如果该信息提示6.65这里有错误,Ok如果你运气好你正好记得或者程序简单一眼能找到6.65表示什么,如果程序很复杂,而且报出6.65的文件是引用该文件,不记得,那么你会困惑这是什么?或许会花大

  • C语言编程技巧 关于const和#define的区别心得

    #define ASPECT_RATIO 1.653 编译器会永远也看不到ASPECT_RATIO这个符号名,因为在源码进入编译器之前,它会被预处理程序去掉,于是ASPECT_RATIO不会加入到符号列表中.如果涉及到这个常量的代码在编译时报错,就会很令人费解,因为报错信息指的是1.653,而不是ASPECT_RATIO.如果ASPECT_RATIO不是在你自己写的头文件中定义的,你就会奇怪1.653是从哪里来的,甚至会花时间跟踪下去.这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不

  • 详解IOS宏与常量的使用(define,const)

    小编给大家整理了关于IOS中宏(define)与常量(const)的正确使用方法,有助于大家更加深入的理解这方面的内容. 当我们想全局共用一些数据时,可以用宏.变量.常量 宏: #define HSCoder @"汉斯哈哈哈" 变量: NSString *HSCoder = @"汉斯哈哈哈"; 常量: 四种写法: static const NSString *HSCoder = @"汉斯哈哈哈"; const NSString *HSCoder

  • PHP中define() 与 const定义常量的区别详解

    本文实例讲述了PHP中define() 与 const定义常量的区别.分享给大家供大家参考,具体如下: 前言 今天在Stackoverflow又看到一个很有趣的文章,所以翻译过后摘了过来.文章是由PHP开发成员之一的NikiC写的,权威性自然毋庸置疑 正文 在PHP5.3中,有两种方法可以定义常量: 使用const关键字 使用define()方法 const FOO = 'BAR'; define('FOO','BAR'); 这两种方式的根本区别在于const会在代码编译时定义一个常量,而def

  • 关于C语言 const 和 define 区别

    目录 一.const 使用 1.const 修饰变量 2.const 修饰指针 3.const 修饰在函数名前面当 4.const 修饰在函数名后面 5.const 修饰函数参数 二.define 使用 1.define 定义常量 2.define 定义函数 3.define 定义多行函数 4.define 防止头文件重复包含 三.const 和 define 区别 四.const 优点 一.const 使用 const 是 constant 的缩写,"恒定不变"的意思.被 const

  • 详解C/C++中const关键字的用法及其与宏常量的比较

    1.const关键字的性质 简单来说:const关键字修饰的变量具有常属性. 即它所修饰的变量不能被修改. 2.修饰局部变量 const int a = 10; int const b = 20; 这两种写法是等价的,都是表示变量的值不能被改变,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了,而且编译器也不允许不赋初值的写法: 在C++中不赋初值的表达一写出来,编译器即报错,且编译不通过. 在C中不赋初值的表达写出来时不报错,编译时只有警告,编译可以通过.而

  • 详解C++中的const关键字及与C语言中const的区别

    const对象默认为文件的局部变量,与其他变量不同,除非特别说明,在全局作用域的const变量时定义该对象的文件局部变量.此变量只存在于那个文件中中,不能别其他文件访问.要是const变量能在其他文件中访问,必须显示的指定extern(c中也是)   当你只在定义该const常量的文件中使用该常量时,c++不给你的const常量分配空间--这也是c++的一种优化措施,没有必要浪费内存空间来存储一个常量,此时const int c = 0:相当于#define c 0:    当在当前文件之外使用

  • C/C++中的typedef和#define详解

    C/C++中的typedef和#define 前言: 在C/C++中,我们平时写程序可能经常会用到typedef关键字和#define宏定义命令,在某些情况下使用它们会达到相同的效果,但是它们是有实质性的区别,一个是C/C++的关键字,一个是C/C++的宏定义命令,typedef用来为一个已有的数据类型起一个别名,而#define是用来定义一个宏定义常量.下面谈谈两者在实际使用中应当注意的地方. 1.typedef关键字 typedef是用来声明类型别名的,在实际编写代码过程使用typedef往

  • C++ const关键字的实例用法

    C++中的const更像编译阶段的#define const int m = 10; int n = m; 变量是要占用内存的,即使被const修饰也不例外.m,n两个变量占用不同的内存,int n = m:表示将m的值赋给n. 在C语言中,编译器会先到m所在的内存取出一份数据,再将这份数据赋给n: 在C++中,编译器会直接将10赋给m,没有读取内存的过程,和int n = 10效果一样. 在C++中的常量更类似于#define命令,是一个值替换的过程,只不过#define是在预处理阶段替换,而

  • C语言中typedef的用法以及#define区别详解

    目录 1.简洁定义 2.为已有类型起别名 为字符数组起别名 为指针起别名 3.typedef 和 #define 的区别 总结 1.简洁定义 C语言允许为一个数据类型起一个新的别名,就像给人起"绰号"一样.而编程中起别名,是为了编程人员编程方便,例如: 定义如下结构体 struct stu { int ID; char name[20]; float score[3]; char *data; }; 要想定义一个结构体变量就得这样写: struct stu Marry://Marry是

随机推荐