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

时间:2015-06-24 15:31:29

标签: c++ pointers reference

我是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-Object。因为Pair是POD(标准布局),所以我可以使用它 3.返回中间对象,封装对* - 听起来非常愚蠢 4.保持NULL:)

我有什么选择吗?

7 个答案:

答案 0 :(得分:4)

尽可能避免使用指针。

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

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

当您没有任何有意义的值返回时,返回boost::optional<Pair>的空实例。调用者需要检查返回的boost::optional<Pair>是否为空或是否包含实例Pair

此外,对于此类情况,find似乎是比get更好的名称 - 因为该函数可能会或可能找不到与关联key相关的有意义的值。

//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;

您可以在代码中同时使用这两项功能 - get可以find实施。

希望有所帮助。

答案 1 :(得分:1)

我认为这些考验是要走的路。由于某些原因,人们低估了他们的需求。

如果你不能抛出异常,你可以将结果作为引用传递并返回bool来指示函数结果 - 如果是真的 - 作为引用传递的对象作为结果有效,如果为false-handle失败,不要使用那个对象。

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

但是再次使用例外。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

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

  2. 空值对象: 如你所说,一个笨拙的解决方法。

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

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

  5. 我认为没有比4更聪明的人。)

答案 4 :(得分:0)

你可以按值返回对吗?因此,值将在堆栈上或任何地方创建,并且您不必担心指针泄漏 等等。

// Interface
Pair get (const char* key) const = 0;

... 

// Usage
Pair result = irp.get();

result.doSomething();

因此,你的看法会像:

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

答案 5 :(得分:0)

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

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

注意:我没有尝试处理内存泄漏 - 请不要发表评论......

#include <stdio.h>

// This is Pair class

class Pair{
public:
    void print() const{
        printf("Hello\n");
    }
};

// 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;
}

答案 6 :(得分: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("%s\n", p->getVal());
   //or
   printf("%s\n", p().getVal());
   //or
   printf("%s\n", p.val().getVal());
}