从指针到引用的升级-如何处理NULL到引用

Upgrade from pointers to references - How to deal with NULL to reference

本文关键字:引用 处理 何处理 NULL 指针      更新时间:2023-10-16

我是C++11的新手,我有这样的类:

class Pair; // defined somewhere...
class IReadOnlyList{
public:
        virtual const Pair *get(const char *key) const = 0;
        inline const Pair *operator[](const char *key) const{
                return get(key);
        };
        inline bool exists(const char *key) const{
                return get(key) != NULL;
        };
};

它工作正常,但我想删除指针。但是,如果我改变Pair *get()Pair &get,则我不能处理不存在的对。

选项是-
1.例外情况
2.NULL对象。因为Pair是POD(标准布局),所以我完全可以使用它
3.返回中间对象,它封装了Pair*-听起来很笨
4.保持NULL:)

我缺少什么选择吗?

尽可能避免使用指针。

在这种情况下,函数可能没有要返回的有意义的值,那么boost::optional可能是一个解决方案:

virtual boost::optional<Pair> get(const char *key) const = 0;

当您没有任何有意义的值要返回时,返回一个空的boost::optional<Pair>实例。调用方需要检查返回的boost::optional<Pair>是空的还是持有实例Pair


此外,在这种情况下,find似乎比get更好;因为函数可能找到或可能找不到具有相关联的CCD_ 9的有意义的值。

//return empty optional when no value found
virtual boost::optional<Pair> find(const char *key) const = 0;

如果您想保留get作为函数名,那么抛出异常将是更好的解决方案:

//throw exception when no value found
virtual const Pair const& get(const char *key) const = 0;

您可以在代码中同时使用这两种功能—CCD_ 11可以根据CCD_。

希望能有所帮助。

我认为期望是前进的方向。由于某些原因,人们低估了他们的需求。

如果你不能抛出异常,那么你可以将结果作为引用传递,并返回bool来标记函数结果——如果是true——作为引用传递的对象作为结果是有效的,如果是false——处理失败,不要使用该对象。

bool get(const char *key, Pair& pair) const{
   if (good){
     pair = goodObject;
     return true;
   }
   return false;
}

但是,再次使用例外。

还有另一种方法-使用空对象。在不知道Pair类的内部数据成员的情况下,无法准确指出Pair的哪个内部数据成员将用于此目的,但在一个项目中,我们使用了这种方法来声明表示错误或无效情况的公共静态常量对象(例如Pair)。调用方可以检查该对象的某些内部数据成员(或调用返回bool的某些安全方法),以确定该对象是否合法。它不像异常那样优雅,也不像NULL指针那样干净,但您总是可以返回一个有效的对象引用,也可以消除调用方是否处理了异常的担忧。

由于您返回的是引用(指针),而不是(!)值:

  1. 例外情况:如果不存在是一个有效的(非异常的)返回值,则不好。

  2. Null对象:正如你提到的,一个笨拙的变通方法。

  3. 返回中间对象,该对象封装结果:这将是一种方法(请参阅boost::可选),但您返回一个引用(指针)

  4. 保留nullptr:在这种情况下简单而聪明。使用任何未测试null未定义返回值的用法(不保留合同)

我认为没有比4更聪明的了。)

您能按值返回Pair吗?因此,值将在堆栈上或任何位置创建,您不必担心指针泄漏等等。

// Interface
Pair get (const char* key) const = 0;
... 
// Usage
Pair result = irp.get();
result.doSomething();

因此,您的get看起来像:

   Pair IRPointerClass::get (const char* key) 
   {
      Pair result;
      ... initialize result using key...
      return result;
   }

除了保留指针和做一些愚蠢的事情之外,我还能想到中间容器类的所有选项。

为了便于编译,我创建了伪Pair类和函数,而不是get()方法。

注意:我没有试图在这里处理内存泄漏-请不要对此发表评论…

#include <stdio.h>
// This is Pair class
class Pair{
public:
    void print() const{
        printf("Hellon");
    }
};
// This is Pair Container class,
// will be interesting to be rewritten with templates
class _Pair{
public:
    _Pair(const Pair *p = NULL) : _pair(p){};
    inline const Pair & val() const{
        return *_pair;
    }
    inline explicit operator bool() const {
        return _pair != NULL;
    }
    inline const Pair & operator *() const {
        return *_pair;
    }
    inline const Pair & operator ()() const {
        return *_pair;
    }
    inline const Pair * operator ->() const {
        return _pair;
    }
private:
    const Pair *_pair;
};
// this is GET() method
const _Pair & getPair(bool a){
    static const _Pair pnull = _Pair();
    if (a){
        return *new _Pair( new Pair() );
    }
    return pnull;
}
// this is usage
int main(int argc, char** argv)
{
    const _Pair & p = getPair(false);
    if (p){
        p().print();
    }
    const _Pair & q = getPair(true);
    if (q){
        // option 1: nice, but additional ()
        // same as boost::optional
        (*q).print();
        // option 2: too long
        const Pair & pp = *q;
        pp.print();
        // option 3: no difference from pointer
        q->print();
        // option 4: using method - looks not bad
        // same as boost::optional
        q.val().print();
        // option 5: nive and yummy :)
        q().print();
    }
    return 0;
}

我确实检查了boost::optional,然后我检查了std::optional的(未来)实验规范,我得出了以下结果:

https://github.com/nmmmnu/HM3/blob/master/std/std_container.h

https://github.com/nmmmnu/HM3/blob/master/std/std_optional.h

然后,因为我会经常使用它,在定义Pair的地方,我也定义了:

class OPair : public std_optional<const Pair>{
public:
    OPair(const Pair *pair = nullptr) : std_optional(pair){};
};

然后IList::get()看起来像这样:

const OPair IArray::get(const char *key) const override{
    uint64_t index;
    if (lookup(key, & index))
        return nullptr;  // LINE 1
    return _data[index]; // LINE 2
}

LINE 1为NULL,它已自动广播到Pair*,然后广播到OPair。LINE 2正在返回Pair*,它已自动广播到OPair。

使用IList看起来像:

OPair p = list.get("bla");
if (p){
   printf("%sn", p->getVal());
   //or
   printf("%sn", p().getVal());
   //or
   printf("%sn", p.val().getVal());
}