条款25:考虑写一个不抛出异常的swap函数

本文探讨了在C++中如何为特定类(如Widget)特化std::swap以提高效率,避免不必要的对象复制。通过在Widget类中添加swap成员函数并创建非成员swap函数来调用成员函数,实现了对Widget对象的高效交换。在处理模板类Widget和WidgetImpl时,面临了特化和访问权限的问题,并提供了相应的解决方案。

考虑如下代码

class WidgetImp1 { //针对Widget数据设计的class,细节不重要
public:
	//...
private:
	int a, b, c;
	std::vector<double>	 v;	
};

class Widget {
public:
	Widget(const Widet& rhs);
	Widget& operator=(const Widget& rhs) {
		//...
		*pImpl = *(rhs.pImpl);
	}
private:
	WidgetImpl* pImpl;	
};

当我们要交换Widget对象值的时候,我们唯一要做的就是交换其成员pImpl指针,但是缺省的swap算法不知道这一点,不但会复制三个widget,还会复制三个WidgetImpl对象,效率十分低下;

我们希望告诉std::swap,当交换Widget时,真正要做的是交换内部的pImpl指针;办法就是将std::swap针对Widget特化

namespace std {
    template<>
    void swap<Widget> (Widget& a, Widget& b){
        swap(a.pImpl, b.pImpl);
    }
}

template<>代表它是针对std::swap的一个全特化,函数名称之后的<Widget>代表这一特化版本系针对"T是Widget"而设计的;通常我们不能够(不被允许)改变std命名空间内的任何东西,但是可以(被允许)为标准templates(如swap)制造特化版本;

上述特化swap还无法通过编译,因为pImpl是私有成员,无法访问,解决办法是令Widget声明一个名为swappublic成员函数做真正的交换工作,然后做特化工作

class Widget {
public:
	//...
	void swap(Widget& other) {
		using std::swap;
		swap(pImpl, other.pImpl);
	}
};

namespace std {
    template<>
    void swap<Widget> (Widget& a, Widget& b){
		a.swap(b);
    }
}

现在我们假设WidgetWidgetImpl都是class templates而非classes:

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

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

我们可以像之前一样在Widget放个swap成员函数,但是在特化std::swap时遇到了麻烦:

namespace std {
	template<typename T>
	void swap< Widget<T> >(Widget<T>& a, Widget<T>& b){ // 错误!
		a.swap(b);
	}
}

我们企图偏特化一个function template,但是C++之允许对class templates偏特化;当你打算偏特化一个function template时惯常做法是简单地为它添加一个重载版本:

namespace std {
	template<typename T>
	void swap(Widget<T>& a, Widget<T>& b){ // 重载版本
		a.swap(b);
	};
}

一般而言,重载function template没有问题,但是std是个特殊的命名空间,客户可以全特化std内的template但不可以添加新的templatesstd里头;解决办法是我们还是声明一个 non-member的swap让它调用member的swap,但不再将那个non-member声明为std::swap的特化版本:

namespace WidgetStuff {
	// ...
	template<typename f>
	class Widget{ //...};
	//...
	template<typename T>
	void swap(Widget<T>& a, Widget<T>& b){ // 这里并不属于std命名空间
		a.swap(b);
	};
}

最后调用swap时应该针对std::swap使用using声明式,然后调用swap并且不带任何"命名空间资格修饰",如下:

template<typename T>
void doSomething(T& a,T& b) {
	using std::swap // 令std::swap在此函数内可用
	//...
	swap(a, b);
	//...
}
<think>好的,我现在需要回答用户关于C++中内置swap函数的使用方法和示例的问题。首先,我得确认用户的问题是什么。他们提到了引用中的swap函数在map中的应用以及Effective C++中的建议,想知道C++是否有内置的swap函数以及如何使用。 首先,我应该回忆C++标准库中的swap函数是否存在。C++标准库确实在<algorithm>和<utility>头文件中提供了swap函数过,根据C++版本的同,可能位置有变化。在C++11之后,更推荐使用<utility>中的swap,而<algorithm>中的可能被保留但逐渐淘汰。需要确认这一点是否正确。 接下来,用户需要使用方法和示例。对于内置类型,比如int、double,swap可以直接使用,比如swap(a, b);。对于容器类,比如vector、map,同样可以直接使用,因为标准库已经为这些类型特化了swap,使其高效交换内部数据指针,而是逐个元素交换。这点需要强调,说明效率高。 然后,用户可能想知道自定义类型如何处理swap。这里应该提到,如果自定义类型没有定义自己的swap函数,std::swap会使用默认的拷贝构造函数和赋值操作符,这可能导致效率低下。因此,建议为自定义类提供特化的swap函数,或者在类内部定义swap方法,并通过ADL(参数依赖查找)来优化。这可能需要引用Effective C++中的条款,比如条款25提到的编异常swap函数。 然后,关于示例代码。应该包括内置类型的例子、标准容器的例子,以及自定义类型的例子。比如,交换两个整数,交换两个vector,交换两个map,以及自定义类的swap实现。需要注意的是,在自定义类中,应该如何重载swap函数,是否要特化std::swap或者提供友元函数等。根据Effective C++的建议,最好在类所在的命名空间内定义swap函数,而是直接特化std模板,除非是对于类模板的情况可能需要部分特化,但C++允许部分特化函数,所以可能需要重载。 另外,要注意swap的使用方法是否正确,是否需要引入std命名空间,或者使用using std::swap;来启用ADL,这样才能正确调用用户定义的swap函数。例如: using std::swap; swap(obj1, obj2); 这样,如果obj1和obj2是自定义类型,并且在其命名空间中有定义swap,则会优先调用该swap,否则使用std::swap。这样可以提高效率和灵活性。 关于示例代码的正确性,要确保语法正确,比如使用正确的头文件,正确的命名空间,以及正确的函数调用方式。例如,std::swap在C++11之后在<utility>中,而之前可能在<algorithm>中。因此,建议包含<utility>并使用std命名空间。 可能还需要提到移动语义(C++11引入)对swap的影响,因为交换指针或资源句柄更为高效,而移动语义允许更高效的资源管理。但用户的问题可能需要深入到这里,过可以简要提及。 最后,总结swap函数的使用场景,强调标准库提供的swap已经足够高效,对于自定义类型需要自行优化,并参考Effective C++的建议来设计。同时,确保示例代码覆盖同情况,并正确展示如何使用。 现在需要组织这些信息,结构清晰,分点说明使用方法,给出示例,并回答相关问题。同时注意引用中的内容,引用[1]提到map的swap函数,引用[2]提到Effective C++的swap实现建议,需要适当引用这些内容。</think>C++标准库中提供了内置的`std::swap`函数,用于交换两个对象的值。以下是具体使用方法和示例: --- ### **1. `std::swap`的基本用法** - **头文件**:需包含`<utility>`(C++11起)或`<algorithm>`(旧版本兼容)。 - **语法**:`std::swap(a, b);` - **作用**:将对象`a`和`b`的值互换。 #### **示例:交换内置类型** ```cpp #include <iostream> #include <utility> // 包含swap函数 int main() { int x = 10, y = 20; std::swap(x, y); std::cout << "x=" << x << ", y=" << y; // 输出:x=20, y=10 return 0; } ``` --- ### **2. 交换标准容器** 标准库容器(如`vector`、`map`)已针对`swap`进行优化,时间复杂度为$O(1)$,仅交换内部指针而非元素[^1]。 #### **示例:交换`std::map`** ```cpp #include <map> #include <utility> int main() { std::map<int, std::string> m1{{1, "apple"}, {2, "banana"}}; std::map<int, std::string> m2{{3, "cherry"}}; std::swap(m1, m2); // 交换m1和m2的内容 // m1变为{{3, "cherry"}}, m2变为{{1, "apple"}, {2, "banana"}} return 0; } ``` --- ### **3. 自定义类型的`swap`优化** 默认`std::swap`通过三次拷贝交换对象,可能效率低下。**优化方法**: - 为自定义类定义专用的`swap`函数,并特化`std::swap`或通过ADL(参数依赖查找)调用。 #### **示例:自定义类的`swap`** ```cpp #include <algorithm> class MyClass { public: int* data; MyClass(int size) { data = new int[size]; } ~MyClass() { delete[] data; } // 定义成员swap函数 void swap(MyClass& other) noexcept { std::swap(data, other.data); // 仅交换指针 } }; // 特化std::swap namespace std { template<> void swap<MyClass>(MyClass& a, MyClass& b) noexcept { a.swap(b); } } // 使用示例 int main() { MyClass obj1(10), obj2(20); std::swap(obj1, obj2); // 高效交换内部指针 return 0; } ``` > **注意**:根据Effective C++条款25,优先在类所在命名空间定义`swap`而非特化`std::swap`[^2]。 --- ### **4. 通过ADL调用自定义`swap`** ```cpp namespace MyNamespace { class MyClass { /* ... */ }; void swap(MyClass& a, MyClass& b) { a.swap(b); } } int main() { MyNamespace::MyClass a, b; using std::swap; // 启用ADL swap(a, b); // 优先调用MyNamespace::swap return 0; } ``` --- ### **
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值