About
RSS

Bit Focus


正确重载 operator=

    以一个整数数组类型为例, 下面的写法
class int_array {
    int* array;
    int size;
public:
    int_array& operator=(int_array const& rhs)
    {
        if (this == &rhs) {
            return *this;
        }

        delete[] array;
        array = new int[];
        std::copy(rhs.array, rhs.array + rhs.size, array);
        size = rhs.size;
        return *this;
    }

    /* other members */
};
是完全错误的.
    设想在下面这样的上下文中
int_array a /* init */;
int_array b;
try {
    b = a;
} catch (std::bad_alloc) {
}
/* ''b'' is bad now! */
    进入 operator= 之后, 先执行了 delete[], 这一句没问题, 如果之后的
array = new int[];
内存分配失败, 这时 b 的成员 array 已经崩坏掉了, 以后再继续使用 b 显然是一件很糟糕的事情.

    为了保持对象状态一致性, 很自然地应该将对象状态的切换放入一个尽可能安全的环境中. 一个方法是使用先复制一份对象, 然后利用绝对安全的 std::swap 将副本与当前对象交换. 假如在复制过程中出错, 并不会破坏当前对象的状态一致性.
class int_array {
public:
    int_array& operator=(int_array const& rhs)
    {
        if (this == &rhs) {
            return *this;
        }

        int_array copy(rhs);
        swap(copy);
        return *this;
    }

    void swap(int_array& other)
    {
        std::swap(array, other.array);
        std::swap(size, other.size);
    }

    /* other members */
};
    一个与之类似的例子是维护程序配置文件, 当程序在退出或者某个其它时机需要将配置写回文件时, 直接清空原来的文件然后提笔并不是一个好主意, 而应该先写入一个临时文件, 再将临时文件 mv 到配置文件.

Permanent Link: ?p=430 Load full text

Post tags:

 Operator Overload
 C++
 RAII

C++ 2011 中的 Left Value 与 Rvalue Reference

#include "某 C++ 概念之序篇"

答: std::stringstream() 产生的临时变量无法匹配类型 std::stringstream&.

    但是 std::stringstream() 可以匹配类型 std::stringstream const&, 也就是说, 如果函数 std::stringstream& operator<<(std::stringstream& os, double d) 的签名和实现作修改成下面这个样子就行了
std::stringstream const& operator<<(std::stringstream const& os, double d)
{
    const_cast<std::stringstream&>(os).operator<<(d);
    return os;
}

    之前扯过 C++ 已经没有语法意义上的左值了, 不过这里似乎有一点点很微妙的, 跟左值很类似的东西, 那就是, C++ 引用. 任何一本 C++ 入门书籍里面都会花一些篇幅来讨论引用, 其基本意义, 无非是某个东西的别名, 因此, 这某个东西对于引用而言显然是至关重要的.
    最基本的一点是, 如果被引用的东西是临时变量 (顺便一提, 如果是字面常量这种比较尴尬的情况, 视同临时变量) 怎么办? 比如之前代码样例 A 中的情况?
    这就牵扯到 C++ 中正统的 left value. 因为 left 已经不再有任何 "左" 相关的意义的, 所以我不太想在本文中把这货称之为 "左值" (实际上英文在这些方面更加灵活, 比如 Copyleft 这词跟 left 也没太大关系, 用来吐槽则非常不错).

    一个东西如果是 left value, 那么它就可以被赋值给 left value 引用, 所谓 left value 引用嘛, 就是就是类似 std::stringstream& 的引用类型, 而暗含 const 的类型的引用则不算
typedef int const int_const;
int_const& this_is_not_a_left_value_reference;
    那究竟什么是 left value 呢?
    一个语句之后仍然存在的变量, 表达式, 或者这样类似的 "具有值属性的什么东西" 就是 left value. 而
std::cout << (std::stringstream() << d).str().length() << std::endl;
这句之后, 显然临时构造的 std::stringstream() 就被析构了, 因此这家伙不算是 left value, 自然也就无法被 std::stringstream& 所引用.
    那常数为什么不算 left value 呢? 当然, 可以认为常数居阴阳之外, 别谈语句之后, 吐核退出, 就算是宇宙毁灭, 什么 π 啊 e 啊还活得好好的; 不过, 从计算机的角度来理解, 常数是 CPU 指令的立即数, 所以别谈语句之后, 只要这条指令结束, 它就没了.

    然而, C++ 一个很邪恶的规定是, 即使一个东西不是 left value, 它仍然可以绑定 const 引用, 比如以下语句都是合法的
std::pair<int, int> const& pair = std::make_pair(0, 1);
std::stringstream const& strm = std::stringstream();
std::string const& x = "Raki suta";
int const& i = 0;
    这样直白地写出来是有点让人觉得不舒服, 不过作为函数调用的参数则好得多, 比如之前的代码片段 B 那样

Permanent Link: ?p=232 Load full text

Post tags:

 Move Semantic
 Operator Overload
 C++
 Right Reference
 Copy Constructor
 Left Value
 C++11

某 C++ 概念之序篇

    C++ 虔诚的信徒们, 请阅读下面的代码, 回答码后的问题
/* 完整样例 A */
#include <iostream>
#include <sstream>

std::stringstream& operator<<(std::stringstream& os, double d)
{
    os.operator<<(d);
    return os;
}

int main()
{
    double d;
    std::cin >> d;
    std::cout << (std::stringstream() << d).str().length() << std::endl;
    return 0;
}
问: 为什么上述代码无法通过编译?
/* 代码片段 B */
#include <map>
#include <string>
#include <stdexcept>

struct key_error
    : public std::logic_error
{
    explicit key_error(std::string const& what)
        : std::logic_error(what)
    {}
};

std::string const& lookup(std::map<std::string, std::string> const& map
                        , std::string const& key)
            throw(key_error)
{
    std::map<std::string, std::string>::const_iterator i = map.find(key);
    if (map.end() == i) {
        throw key_error("key ``" + key + "'' not found in map");
    }
    return i->second;
}
问: 上述代码中的 lookup 函数有什么隐患?

答案已揭晓, 点此查看, 谢谢关注.

Permanent Link: ?p=222 Load full text

Post tags:

 C++11
 Copy Constructor
 Move Semantic
 C++
 Operator Overload
 Left Value
 Right Reference

C++ 到底怎么使用流来输出对象

    书上写的做法当然是类似
struct my_type {
    void set_x(int xx);
    void set_y(double yy);

    my_type(int xx, double yy)
        : x(xx)
        , y(yy)
    {}

    friend std::ostream& operator<<(std::ostream& os, my_type const&);
private:
    int x;
    double y;
};

std::ostream& operator<<(std::ostream& os, my_type const& m)
{
    return os << m.x << ' ' << m.y;
}
    一般使用这样没问题, 不过在想要写得很省略的用况下, 这样就很纠结
my_type m(10, 20.0);
std::stringstream ss;
std::string image = (ss << m).str(); // WRONG!
因为 operator<< 返回的类型已经变成没有 str() 取得字符串函数的基类 std::ostream& 了. 看起来要什么流类型进来什么流类型出才行, 比如
struct my_type {
    void set_x(int xx);
    void set_y(double yy);

    my_type(int xx, double yy)
        : x(xx)
        , y(yy)
    {}

    friend template<typename _OS>
    _OS& operator<<(_OS& os, my_type const&); // WRONG
private:
    int x;
    double y;
};

template <typename _OS>
_OS& operator<<(_OS& os, my_type const& m)
{
    os << m.x << ' ' << m.y;
    return os;
}
不过很遗憾, 泛型函数无法做朋友
    那要不先把 my_type 对象弄成字符串了, 再把字符串输出好了
struct my_type {
    void set_x(int xx);
    void set_y(double yy);

    my_type(int xx, double yy)
        : x(xx)
        , y(yy)
    {}

    std::string str() const
    {
        std::stringstream ss;
        ss << m.x << ' ' << m.y;
        return ss.str();
    }
private:
    int x;
    double y;
};

template <typename _OS>
_OS& operator<<(_OS& os, my_type const& m) // ACTUALLY NOT NEEDED ANY MORE
{
    os << m.str();
    return os;
}
这看起来多疼啊.

    一个可能的方案是, 为每个自定义类型实现 str() 成员函数 (有必要的还可以虚一下), 然后全局声明一个
template <typename _OS, typename _T>
_OS& operator<<(_OS& os, _T const& t)
{
    os << t.str();
    return os;
}
也许这样会好很多.

Permanent Link: ?p=177 Load full text

Post tags:

 C++
 Operator Overload
 Output Stream

C++ 中的左值

已经转移到 http://zlo.gs/p/neuront/left-hand-value-in-cpp

Permanent Link: ?p=85 Load full text

Post tags:

 C++
 Left Value
 Operator Overload


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site