C/C++首页投稿(暂停使用,暂停投稿)C++

C++程序设计一些深层次的细节

2017-03-08  本文已影响77人  alex_zhou

本文预览:

前言

本篇会提到一些琐碎的东西,虽然有一些在我们编写代码的过程中不经常用到,但是都是一些帮助你理解C++底层次的东西。网上有人开玩笑说,一个java程序员学了三年,师傅对他说,你已经精通java了,可以下山了;一个C++程序员,学了十年,师傅对他说,你可以下山了,但是不要说你精通C++。这当然只是个笑话,但是从侧面反映出一个深刻的道理,那就是C++坑大水深,入坑需谨慎~。既然你已经入坑了,不想就此自费武功,那就好好修炼内功吧。

转换函数 与 non-explict-one-argument Ctor

转换函数是什么鬼?类型转换嘛,顾名思义。转换函数记住一句话就好了,有转出去和转进来之分。
举个例子:设计一个分数类 x/y

class Fraction {//这个分数类还是非常简单的嘛
public:
    Fraction(int x, int y = 1):_x(x),_y(y){}
    inline int x() const {return _x;}
    inline int y() const {return _y;}
    operator double() const
    {
        return (double)(_x ) / (double)(_y);
    }
    Fraction operator+(const Fraction& f)
    {
        return Fraction(...);
    }
private:
    int _x; //分子
    int _y; //分母
};

分数类设计好了,使用的时候出现问题了:

Fraction f(3,5);
double d = 4 + f;

一个整数加一个分数,能编译通过吗?我们是不是还需要重载一个+操作符来来实现运算呢?现在好像是不用了,编译器能通过编译,计算结果也是正确的。你也许已经注意到了:

    operator double() const
    {
        return (double)(_x ) / (double)(_y);
    }

这就是转换函数的语法,没有返回值类型,函数名称就已经说明了你需要转换出去的类型。C++编译器会这么在遇到 4+f的时候首先会去寻找operator+(int),都没有int可以稳定转成double, operator+(double)肯定是有的,double+double类型能通过吗?找找看Fraction能转成double类型吗?operator double(),定义了转换到double类型的函数,可以转换。

现在我们把转换函数去掉,Fraction不能转成double了:

Fraction f(3,5);
double d = f + 4;

我们也没有operator+(const double) ,但是编译能过吗?我们重载了一个operator+(const Fraction&),这个必须是要有的,没有重载+肯定是编译不过的,也就是说,我们这次的+,肯定是用到了operator+(const Fraction),那么好了,如果能编译通过,肯定是int转成了Fraction类型了。但是,我们不可能去修改C++语言内核,在int里也加一个转换函数吧。为题出在哪里了?它怎么自动转的,这就用到了non-explict-one-argument Ctor, 自动调用了Fraction的构造函数,把int转换成了Fraction。

注意:

Fraction(int x, int y = 1):_x(x),_y(y){}
// explict Fraction(int x, int y = 1):_x(x),_y(y){}

这种构造函数叫做non-explict-one-argument Ctor,前面没有explict修饰,并且只有一个实参,第二个参数是有默认值的,如果没有默认值就不能叫one-argument。如果没有加explict编译器是可以自行调用,把其他类型转进来的。加了explict,编译就不通过了。

pointer-like class

pointer-like class:把class设计出来当做指针用。在智能指针和迭代器都是这么设计的。

  1. 智能指针的设计
智能指针的设计

智能指针被C++设计用来管内存,写法相当固定,必须要重载的和->,解引用这个应该都是没有什么困惑,但是问题在->这个问题就出来了,sp->method()调用返回的是T啊,在第一次已经使用了->,返回的T*还怎么调用method(),C++语法定义了->有继续作用的特性。这个问题就解决了。我们第一次虽然使用了->,但是由于C++语法规定,->能继续作用,因此,智能指针这么设计是没有问题的。

  1. 迭代器的设计

迭代器在STL里大部分都被设计成了class,迭代器的使用方法也很像是指针,我们以list的迭代器来看,是如何重载指针的解引用和调用操作符的。

迭代器的设计

在list中*操作符将获取元素对象,也就是list_no
![Uploading 屏幕快照 2017-03-08 16.00.50_078916.png . . .]
de<T>的data部分;->操作符获得data的指针。这样,我们就能使用迭代器,直接获取list元素对象和调用相应的方法了。

function-like class 所谓仿函数

function-like class: 将类设计出来,当做函数使用,又叫仿函数。其实质就是在里面重载了()操作符

仿函数在STL中

STL里面的算法less实现:

template <class T>
struct less
{
     bool operator()(const T& x, const T& y) const {return x < y; }
}

这是算法吗?分明就是一个class,这是一个class吗?这不是仿函数吗?自己好像很少用到仿函数吧,C++11里面出现了lamda表达式之后对仿函数简直就是秒杀有没有。但是它确实在STL里面有大量的应用。

    bool ret = less<int>()(2,1);        //第一个()表示是一个临时对象,第二个()才真正调用了operator()
    cout<<ret<<endl;
    
    float d = plus<float>()(1.2,2.1);
    cout<<d<<endl;

以上是使用示例。

Reference经验谈

如果从内存角度看一个变量,那么变量大概可以分为三类:

int x = 0;
int& r = x;
cout<<x<<endl;
cout<<&x<<endl;
cout<<sizeof(x)<<endl;

cout<<r<<endl;
cout<<&r<<endl;
cout<<sizeof(r)<<endl;

你所看到的r和x所有的都是一样的,于是我们称reference是object的别名。但是它的内部实现是指针,但是它屏蔽了指针的概念和用法,在用法上和它所引用的对象是一样的。

关于引用需要知道:

  1. 一个引用变量在创建的时候要有初始值;
  2. 通常不用做变量声明,而作为参数传递;
  3. java里面的所有变量都是reference;
  4. 修改引用,也会修改它所代表的对象;
  5. 实现是指针,逻辑上我们理解为代表或者别名。
上一篇下一篇

猜你喜欢

热点阅读