C++程序员经常问的11个问题

2023-05-16

下面的这些要点是对所有的C++程序员都适用的。我之所以说它们是最重要的,是因为这些要点中提到的是你通常在C++书中或网站上无法找到的。如:指向成员的指针,这是许多资料中都不愿提到的地方,也是经常出错的地方,甚至是对一些高级的C++程序员也是如此。
  这里的要点不仅仅是解释怎样写出更好的代码,更多的是展现出语言规则里面的东西。很显然,它们对C++程序员来说是永久的好资料。我相信这一篇文章会使你收获不小。

  首先,我把一些由不同层次的C++程序员经常问的问题归到一起。我惊奇的发现有很多是有经验的程序员都还没意识到 .h 符号是否还应该出现在标准头文件中。

 

要点1: 还是 ?

   很多C++程序员还在使用而不是用更新的标准的库。这两者都有什么不同呢?首 先,5年前我们就开始反对把.h符号继续用在标准的头文件中。继续使用过时的规则可不是个好的方法。从功能性的角度来讲,< iostream>包含了一系列模板化的I/O类,相反地只仅仅是支持字符流。另外,输入输出流的C++标准 规范接口在一些微妙的细节上都已改进,因此,和在接口和执行上都是不同的。最后, 的各组成都是以STL的形式声明的,然而的各组成都是声明成全局型的。

  因为这些实质上的不同,你不能在一个程序中混淆使用这两个库。做为一种习惯,在新的代码中一般使用,但如果你处理的是过去编写的代码,为了继承可以用继续用旧保持代码的一致性。  


要点2:用引用传递参数时应注意的地方

  在用引用传递参数时,最好把引用声明为const类型。这样做的好处是:告诉程序不能修改这个参数。在下面的这个例子中函数f()就是传递的引用:
void f(const int & i);
int main()
{
 f(2); /* OK */
}
   这个程序传递一个参数2给f()。在运行时,C++创建一个值为2的int类型的临时变量,并传递它的引用给f().这个临时变量和它的引用从f()被 调用开始被创建并存在直到函数返回。返回时,就被马上删除。注意,如果我们不在引用前加上const限定词,则函数f()可能会更改它参数的值,更可能会 使程序产生意想不到的行为。所以,别忘了const。

  这个要点也适用于用户定义的对象。你可以给临时对象也加上引用如果是const类型:
struct A{};
void f(const A& a);
int main()
{
 f(A()); // OK,传递的是一个临时A的const引用
}

 

要点3:“逗号分离”表达形式

 “逗号分离”表达形式是从C继承来的,使用在for-和while-循环中。当然,这条语法规则被认为是不直观的。首先,我们来看看什么是“逗号分离”表达形式。

  一个表达式由一个或多个其它表达式构成,由逗号分开,如:
 if(++x, --y, cin.good()) //三个表达式
  这个if条件包含了三个由逗号分离的表达式。C++会计算每个表达式,但完整的“逗号分离”表达式的结果是最右边表达式的值。因此,仅当cin.good()返回true时,if条件的值才是true。下面是另一个例子:
int j=10;
int i=0;
while( ++i, --j)
{
 //直到j=0时,循环结束,在循环时,i不断自加
}

 

要点4,使用全局对象的构造函数在程序启动前调用函数

   有一些应用程序需要在主程序启动前调用其它函数。如:转态过程函数、登记功能函数都是必须在实际程序运行前被调用的。最简单的办法是通过一个全局对象的 构造函数来调用这些函数。因为全局对象都是在主程序开始前被构造,这些函数都将会在main()之前返回结果。如:
class Logger
{
 public:
 Logger()
  {
   activate_log();//译者注:在构造函数中调用你需要先运行的函数
  }
};
Logger log; //一个全局实例
int main()
{
 record * prec=read_log();//译者注:读取log文件数据
 //.. 程序代码
}
  全局对象log在main()运行之前被构造,log调用了函数activate_log()。从而,当main()开始执行时,它就可以从log文件中读取数据。

  毫无疑问地,在C++编程中内存管理是最复杂和最容易出现bug的地方。直接访问原始内存、动态分配存储和最大限度的发挥C++指令效率,都使你必须尽力避免有关内存的bug。
  
要点5:避免使用复杂构造的指向函数的指针

  指向函数的指针是C++中可读性最差的语法之一。你能告诉我下面语句的意思吗?
void (*p[10]) (void (*)());
   P是一个“由10个指针构成的指向一个返回void类型且指向另一个无返回和无运算的函数的数组”。这个麻烦的语法真是让人难以辨认,不是吗?你其实可 以简单的通过typedef来声明相当于上面语句的函数。首先,使用typedef声明“指向一个无返回和无运算的函数的指针”:
typedef void (*pfv)();
  接着,声明“另一个指向无返回且使用pfv的函数指针”:
typedef void (*pf_taking_pfv) (pfv);
  现在,声明一个由10个上面这样的指针构成的数组:
pf_taking_pfv p[10];
  与void (*p[10]) (void (*)())达到同样效果。但这样是不是更具有可读性了!

 

要点6:指向成员的指针

  一个类有两种基本的成员:函数成员和数据成员。同样的,指向成员的指针也有两种:指向函数成员的指针和指向数据成员的指针。后则其实并不常用,因为类一般是不含有公共数据成员的,仅当用在继承用C写的代码时协调结构(struct)和类(class)时才会用到。

   指向成员的指针是C++语法中最难以理解的构造之一,但是这也是一个C++最强大的特性。它可以让你调用一个类的函数成员而不必知道这个函数的名字。这 一个非常敏捷的调用工具。同样的,你也可以通过使用指向数据成员的指针来检查并改变这个数据而不必知道它的成员名字。
  指向数据成员的指针

  尽管刚开始时,指向成员的指针的语法会使你有一点点的迷惑,但你不久会发现它其实同普通的指针差不多,只不过是*号的前面多了::符号和类的名字,例:定义一个指向int型的指针:
int * pi;
  定义一个指向为int型的类的数据成员:
int A::*pmi; //pmi是指向类A的一个int型的成员
  你可以这样初始化它:
class A
{
 public:
 int num;
 int x;
};
int A::*pmi = & A::num;
  上面的代码是声明一个指向类A的一个int型的num成员并将它初始化为这个num成员的地址.通过在pmi前面加上*你就可以使用和更改类A的num成员的值:
A a1, a2;
int n=a1.*pmi; //把a1.num赋值给n
a1.*pmi=5; // 把5赋值给a1.num
a2.*pmi=6; // 把6赋值给6a2.num
  如果你定义了一个指向类A的指针,那么上面的操作你必须用 ->*操作符代替:
A * pa=new A;
int n=pa->*pmi;
pa->*pmi=5;

  指向函数成员的指针

  它由函数成员所返回的数据类型构成,类名后跟上::符号、指针名和函数的参数列表。举个例子:一个指向类A的函数成员(该函数返回int类型)的指针:
class A
{
 public:
 int func ();
};
int (A::*pmf) ();
  上面的定义也就是说pmf是一个指向类A的函数成员func()的指针.实际上,这个指针和一个普通的指向函数的指针没什么不同,只是它包含了类的名字和::符号。你可以在在任何使用*pmf的地方调用这个函数
func():
pmf=&A::func;
A a;
(a.*pmf)(); //调用a.func()
  如果你先定义了一个指向对象的指针,那么上面的操作要用->*代替:
A *pa=&a;
(pa->*pmf)(); //调用pa->func()
  指向函数成员的指针要考虑多态性。所以,当你通过指针调用一个虚函数成员时,这个调用将会被动态回收。另一个需要注意的地方,你不能取一个类的构造函数和析构函数的地址。

 

要点7、避免产生内存碎片

   经常会有这样的情况:你的应用程序每运行一次时就因为程序自身缺陷而产生内存漏洞而泄漏内存,而你又在周期性地重复着你的程序,结果可想而知,它也会使 系统崩溃。但怎样做才能预防呢?首先,尽量少使用动态内存。在大多数情况下,你可能使用静态或自动存储或者是STL容器。第二,尽量分配大块的内存而不是 一次只分配少量内存。举个例子:一次分配一个数组实例所需的内存,而不是一次只分配一个数组元素的内存。

 

要点8、是delete还是delete[]

  在程序员中有个荒诞的说法:使用delete来代替delete[]删除数组类型时是可以的!
  举个例子吧:
 int *p=new int[10];
 delete p; //错误,应该是:delete[] p
  上面的程序是完全错误的。事实上,在一个平台上使用delete代替delete[]的应用程序也许不会造成系统崩溃,但那纯粹是运气。你不能保证你的应用程序是不是会在另一个编译器上编译,在另一个平台上运行,所以还是请使用delete[]。

 

要点9、优化成员的排列

  一个类的大小可以被下面的方式改变:
struct A
{
 bool a;
 int b;
 bool c;
}; //sizeof (A) == 12
   在我的电脑上sizeof (A) 等于12。这个结果可能会让你吃惊,因为A的成员总数是6个字节:1+4+1个字节。那另6字节是哪儿来的?编译器在每个bool成员后面都插入了3个填 充字节以保证每个成员都是按4字节排列,以便分界。你可以减少A的大小,通过以下方式:
struct B
{
 bool a;
 bool c;
 int b;
}; // sizeof (B) == 8
  这一次,编译器只在成员c后插入了2个字节。因为b占了4个字节,所以就很自然地把它当作一个字的形式排列,而a和c的大小1+1=2,再加上2个字节就刚好按两个字的形式排列B。

 

要点10、为什么继承一个没有虚析构函数的类是危险的?

   一个没有虚析构函数的类意味着不能做为一个基类。如std::string, std::complex, 和 std::vector 都是这样的。为什么继承一个没有虚析构函数的类是危险的?当你公有继承创建一个从基类继承的相关类时,指向新类对象中的指针和引用实际上都指向了起源的对 象。因为析构函数不是虚函数,所以当你delete一个这样的类时,C++就不会调用析构函数链。举个例子说明:
class A
{
 public:
 ~A() // 不是虚函数
 {
 // ...
 }
};
class B: public A //错; A没有虚析构函数
{
 public:
 ~B()
 {
 // ...
 }
};
int main()
{
 A * p = new B; //看上去是对的
 delete p; //错,B的析构函没有被调用
}

 

要点11、以友元类声明嵌套的类

  当你以友元类声明一个嵌套的类时,把友元声明放在嵌套类声明的后面,而不前面。
class A
{
 private:
 int i;
 public:
 class B //嵌套类声明在前
 {
  public:
  B(A & a) { a.i=0;};
 };
 friend class B;//友元类声明
};

  如果你把友元类声明放在声明嵌套类的前面,编译器将抛弃友元类后的其它声明。

转载自:https://blog.csdn.net/ithomer/article/details/5019203

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++程序员经常问的11个问题 的相关文章

  • tars源码分析之27

    openssl的read write handshake if TARS SSL include lt openssl ssl h gt include lt openssl err h gt include 34 util tc open
  • 蓝桥杯,进决赛了

    大家好 xff0c 我是涛哥 前几天一位读者联系我并表达感谢 xff0c 经仔细了解才知道 xff0c 他是通过我的文章了解到蓝桥杯大赛的 xff0c 第一次参赛经验不足 xff0c 成绩一般 xff0c 第二次参赛就进入决赛 xff0c
  • 如何用adb连接android手机?(我的亲自经历)------ 顺便说说unable to connect to 192.168.1.100:5555的原因和解决方法

    adb是什么呢 xff1f 我就不多说了 xff0c 对于搞android开发的人来说 xff0c 一定不陌生 本文讲述如何用adb来连接手机 利用adb来连接手机 xff0c 有两种方式 1 wifi 2 usb 前提条件 xff1a 下
  • CSRF攻击与防御(写得非常好)

    CSRF概念 xff1a CSRF跨站点请求伪造 Cross Site Request Forgery xff0c 跟XSS攻击一样 xff0c 存在巨大的危害性 xff0c 你可以这样来理解 xff1a 攻击者盗用了你的身份 xff0c
  • “undefined reference to“ 问题汇总及解决方法 ------非常非常好的一篇文章

    在实际编译代码的过程中 xff0c 我们经常会遇到 34 undefined reference to 34 的问题 xff0c 简单的可以轻易地解决 xff0c 但有些却隐藏得很深 xff0c 需要花费大量的时间去排查 工作中遇到了各色各
  • 为什么能抓到网站https传输的明文密码?------顺便说说“知乎”和“支付宝”的安全性对比

    在多数人看来 xff0c https是安全的 xff0c 因为https和secure http嘛 xff0c 真的是这样吗 xff1f 一些人认为 xff0c https是加密传输 xff0c 所以抓到包也没有用 xff0c 是密文的 真
  • VMware Ubuntu安装详细过程(非常靠谱)

    不是每一个程序员都必须玩过linux xff0c 只是博主觉得现在的很多服务器都是linux系统的 xff0c 而自己属于那种前端也搞 xff0c 后台也搞 xff0c 对框架搭建也感兴趣 xff0c 但是很多生产上的框架和工具都是安装在服
  • linux命令行无故换行的恼人问题

    在敲linux命令时 xff0c 明明本行远远没有满 xff0c 就自动换行了 xff0c 而且还覆盖 xff0c 颇为恼人 在网上找了很多地方 xff0c 也没有比较好的解决方法 xff0c 勉强忍受了一段时间 最近 xff0c 偶然看到
  • ZeroMQ“发布/订阅”模型的C++代码

    ZeroMQ环境的搭建就不说了 xff0c 之前已经说过 来看ZeroMQ的 发布 订阅 模型的C 43 43 代码 xff1a pub cpp代码为 xff1a include lt stdio h gt include lt stdli
  • CentOS7启动vncserver命令

    在CentOS7上 xff0c 使用yum安装vncserver xff0c 默认会安装tigerVNC xff0c 安装配置完成后 xff0c 启动vncserver的命令如下 xff08 通过systemctl启动 xff09 xff1
  • ubuntu安装cuda8.0+tensorflow+pytorch

    Data 2017 7 23 Author cjh 1 下载cuda8 0 https developer nvidia com cuda downloads 本人选择的是deb local xff0c 网上很多教程都是根据runfile
  • 【神经网络并行训练(上)】基于MapReduce的并行算法的实现

    前言 最近看了一些基于MapReduce的神经网络并行训练方面的论文 xff0c 老师让我自己去实现一下 xff0c 更深入的体会其中的原理 MapReduce是基于java语言的框架 xff0c 于是一开始想用java写深度学习代码 但是
  • dicom文件与bmp和jpg文件的相互转化

    前面工作需要 xff0c 将dicom医学文件转化为普通图像 xff0c 如bmp xff0c jpg等 xff0c 中间应用到了CxImage x64和dcmtk包 实现过程中 xff0c 遇到了不少麻烦 xff0c 现将相关过程分享如下
  • Caffe 安装OpenCV-2.4.13

    有一段时间没写博客了 xff0c 主要是有一段时间没弄Ubuntu的Caffe配置了 最近NVIDIA把驱动啥的都升级到了CUDA 8 0版本 xff0c 安装OpenCV的时候会出现版本不兼容的问题 相信大家的OpenCV安装包都是Git
  • Nginx配置 https 证书

    1 阿里云创建免费SSL证书 2 证书申请 3 填写相关信息执行下一步 4 云服务器ECS安全组开放443端口 安全 5 下载证书 这里选择的是Nginx 6 证书上传服务器 1 把证书存放在指定目录得到两个文件后缀为 key 一个是 pe
  • ubuntu16.04 装机3:安装xrdp, 远程界面化操作

    前言 xff1a 本文教程自己试过多次 xff0c 在ubuntu16 04上都安装成功了 但是有可能在ubuntu18上会有些问题 经同学推荐 xff0c 在ubuntu18上安装xrdp xff0c 可以参考 xff1a Ubuntu1
  • 对 pretext tasks 的理解

    在读一些自监督学习算法的时候 xff0c 遇到了pretext tasks这个术语 xff0c 所以对这个术语的含义做了下了解 pretext tasks 通常被翻译作 前置任务 或 代理任务 xff0c 有时也用 surrogate ta
  • ubuntu下中文文件名乱码

    windows下的中文文件名拷贝到ubuntu下面以后 xff0c 文件名直接变成乱码 xff0c 原因为windows下的文件名以GBK编码 xff0c 而Ubuntu下的文件 名为utf span class hljs subst sp
  • 树莓派3B+的基础配置

    一 文章背景 本文写于北京时间2019年11月13日 我前两天刚到杭州导师的实验室 xff0c 很多事情都还是一窍不通 方向是物联网相关 xff0c 最近在结合 物联网应用快速开发 从创意到原型 学习一些物联网的基础知识 xff0c 书中的
  • Centos在vm中设置网络环境、防火墙设置

    本次实践采用Centos 7 的操作系统 新建虚拟机选择安装文件这里就不多说了 xff0c 从选择好配置之后 xff0c 启动安装之前说起 1 添加网卡 在系统安装之前一定要设置下虚拟机的硬件配置 xff0c 在硬件设置中添加网卡 xff0

随机推荐