考虑实现一个不抛异常的swap

Effective C++:参考自harttle land

类的swap实现与STL容器是一致的:提供swap成员函数, 并特化std::swap来调用那个成员函数

 1 class Widget {
 2 public:       
 3   void swap(Widget& other){
 4     using std::swap;          // 使得`std::swap`在该作用域内可见
 5     swap(pImpl, other.pImpl);
 6   }
 7 };
 8 
 9 namespace std {
10   template<>
11   void swap<Widget>(Widget& a, Widget& b){
12       a.swap(b);              // 调用成员函数
13   }
14 }

类模板的Swap

1.下面代码不能通过编译。C++允许偏特化类模板,却不允许偏特化函数模板

template<typename T>
class WidgetImpl { ... };

template<typename T>
class Widget { ... };

namespace std {
    template<typename T>
    // swap后的尖括号表示这是一个特化,而非重载。
    // swap<>中的类型列表为template<>中的类型列表的一个特例。
    void swap<Widget<T> >(Widget<T>& a, Widget<T>& b){
        a.swap(b); 
    }
}

2. 不能向STL里面添加新的东西,重载版本。C++标准中,客户只能特化std中的模板,但不允许在std命名空间中添加任何新的模板,会引发未定义

namespace std {
    template<typename T>
    // 注意swap后面没有尖括号,这是一个新的模板函数。
    // 由于当前命名空间已经有同名函数了,所以算函数重载。
    void swap(Widget<T>& a, Widget<T>& b){
        a.swap(b); 
    }
}

正确做法是不在std下添加swap函数了,把swap定义在Widget所在的命名空间中,或直接在global命名空间:

namespace WidgetStuff {
   template<typename T> 
   class Widget {
   public:       
      void swap(Widget& other){
        using std::swap;         
        swap(pImpl, other.pImpl);
  }
};
  
    template<typename T> 
    void swap(Widget<T>& a, Widget<T>& b){
        a.swap(b);
    }
}

似乎类的Swap也只需要在同一命名空间下定义swap函数,而不必特化std::swap。 但是!有人喜欢直接写std::swap(w1, w2),调用std::swap,WidgetStuff::swap不会得到调用。特化std::swap可以让你的类更加健壮。此时,C++编译器会优先调用指定了T的std::swap特化版,其次是T所在命名空间下的对应swap函数, 最后才会匹配std::swap的默认实现。

应用:

赋值构造函数的swap写法:解决自己赋值自己的问题

class Widget
{
public:
    Widget():id(0), size(0), data(NULL){}
    Widget(int val)
    {
        id = val;
        size = id+1;
        data = new int [size];
    }
    Widget(const Widget &rhs) //deep copy
    {
        id = rhs.id;
        size = rhs.size;
        data = new int[size];
        memcpy(data, rhs.data, sizeof(int)*size);
    }
    friend void swap(Widget & lhs, Widget &rhs)
    {
        using std::swap;
        swap(lhs.id, rhs.id);
        swap(lhs.size, rhs.size);
        swap(lhs.data, rhs.data);
    }
  /* Widget
& operator = (Widget rhs) //pass by value, copy构造后swap { swap(*this, rhs); return *this; } */ Widget& operator= (const Widget &rhs) //pass by 引用 { Widget tmp(rhs); swap(*this, tmp); return *this; } ~Widget() { std::cout<<"id:"<< id << " destructor called..." << std::endl; if(data != NULL) { delete [] data; data = NULL; } } int getId() const { return id; } int getSize() const { return size; } private: int *data; int id; int size; };

如何实现Swap呢?总结一下:

  1. 提供一个更加高效的,不抛异常的共有成员函数(比如Widget::swap)。
  2. 在你类(或类模板)的同一命名空间下提供非成员函数swap,调用你的成员函数。
  3. 如果你写的是类而不是类模板,请偏特化std::swap,同样应当调用你的成员函数。
  4. 调用时,请首先用using使std::swap可见,然后直接调用swap

转载于:https://www.cnblogs.com/demian/p/9301679.html

枚举for循环实现四则运算的方法如下: 1. 首先定义一个枚举类型,用来表示四则运算的四种操作:加、减、乘、除。 2. 在运算时,用一个for循环不断地枚举每一位数和每一种运算符的可能性。在每一次迭代中,分别尝试用当前运算符对下一位数进行计算,得到一个中间结果。然后,用同样的方式对中间结果进行下一次运算,直到最后得到最终答案。 3. 对于乘除法,需要特别注意除数不为0的情况。如果出现除数为0的情况,则应该异常或者给出错误提示。 4. 在计算过程中,需要使用临时变量来记录当前的运算符和中间结果。 示例代码如下: ``` enum Operator { ADD, SUB, MUL, DIV }; double calc(double x, double y, Operator op) { switch (op) { case ADD: return x + y; case SUB: return x - y; case MUL: return x * y; case DIV: return x / y; } } double eval(const vector<double>& nums, const vector<Operator>& ops) { double res = nums[0]; for (int i = 0; i < ops.size(); i++) { res = calc(res, nums[i + 1], ops[i]); } return res; } void solve(const vector<double>& nums, const vector<Operator>& ops, int pos, double& ans) { if (pos == ops.size()) { ans = eval(nums, ops); return; } for (int i = pos; i < ops.size(); i++) { swap(ops[pos], ops[i]); solve(nums, ops, pos + 1, ans); swap(ops[pos], ops[i]); } } int main() { vector<double> nums = {1, 2, 3, 4}; vector<Operator> ops = {ADD, SUB, MUL, DIV}; double ans = 0; solve(nums, ops, 0, ans); cout << ans << endl; // 输出 0.5 (1/2) return 0; } ``` 注:以上代码只是一个简单的示例,实际应用中还需要考虑更多的细节问题,比如运算符的优先级、括号的处理等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值