1. 程式人生 > 其它 >C++進階——C++標準庫進階3

C++進階——C++標準庫進階3

技術標籤:C++進階c++stl

十三、關聯容器的等價與比較函式的返回值

兩個物件是否相等是基於operator==。因為operator==可以自定義,所以,兩個物件相等,並不意味著物件所有的資料成員都相等

而關聯容器對相同元素的定義是等價,預設是基於operator<。比如有個map<string, int>物件,該物件key的排序方式預設就是operator<,a與b等價關係可以如下表示:

!(a<b) && !(b<a)

上述表示式的含義是:按照一定的排序規則,a,b中的任何一個都不在另一個前面,則二者等價

如果排序規則不是operator<,則兩個元素等價的條件是

!keycomp(x,y) && !keycomp(y,x)

其中,keycomp表示key的排序規則

示例

class comp
{
public:
	bool operator()(const string &a, const string &b)
	{
        if(a.size()!=b.size()) {
            return true;
        }
        for (unsigned i=0;i<a.size();++i) {
            if (tolower(a[i])!=tolower(b[i])) {
                return true;
            }
        }

        return false;
	}
};

int main(int argc, char const *argv[])
{
	set<string, comp> s;
	pair<set<string, comp>::iterator, bool> res1=s.insert("abc");
	auto res2=s.insert("ABC");

	auto res3=s.insert("AB");
	cout<<res3.second<<endl;

	cout<<res1.second<<","<<res2.second<<endl;
	cout<<(s.find("ABC")!=s.end())<<endl;
	cout<<(find(s.begin(), s.end(), "ABC")!=s.end())<<endl;
	
	return 0;
}

通過輸出結果可知,等價的判定規則生效,當字串abc被insert被新增到容器中後,因為ABC和abc滿足等價條件,為了保證set中所有的元素都是不等價的,所以,ABC沒有被新增到容器中

但是又因為abc和ABC是等價的,所以s.find("ABC")!=s.end()的結果為true,說明查詢成功

而標準庫的find函式是基於相等來進行查詢的,所以find(s.begin(), s.end(), "ABC")直接返回了尾後迭代器,沒查詢到ABC

上面的程式碼,ABC和abc都會讓函式呼叫運算子返回false,從而滿足!comp("ABC", "abc") && !comp("abc", "ABC")的表示式結果為true,進而判定二者等價,從而二者只有一個被新增到容器中,如果上述程式碼的第7,11行返回false,那麼就會判定ABC和abc不等價,就會將ABC和abc都插入到容器中,違背了忽略大小寫的實現原則

十四、讓關聯容器的比較規則在等值的情況下返回false。

如果關聯容器的比較規則在等值的情況下返回true,就有可能使set中出現等值的元素,破壞set

示例

bool comp(int a, int b)
{
	return a<=b;
}

int main(int argc, char const *argv[])
{
	set<int, bool(*)(int, int)> s;
	s.insert(10);
	s.insert(10);
	return 0;
}

原因:當插入第二個10時,會根據比較規則判定這兩個10是否等價,因為!(10<=10) && !(10>=10) 的表示式的結果為false,所以判定10和10不等價(相等的值但是卻不等價),所以將第二個10插入到set中,所以set中有兩個重複的10,出現了重複的key,破壞了set,所以出現段錯誤

解決辦法就是讓比較規則在等值的情況下返回false,進而滿足等價的條件。此外,讓比較規則在等值的情況下返回false是任何一個嚴格弱序的函式必須遵守的規則

上述兩個原則說明,關聯容器的排序規則是基本等價而不是相等,兩個元素是否等價的判定條件完全依賴比較函式,可以自由實現,但是可以自由實現的前提是不能突破關聯容器的原則

十五、當關聯容器的key的型別為指標時,要指定比較函式

如果關聯容器中的元素是指標,那麼關聯容器預設是按照指標的16進位制的地址值的大小進行升序排列,而不是根據指標指向的物件的operator<進行排列

示例

int main(int argc, char const *argv[])
{
	set<string *> s;
	s.insert(new string("qwer"));
	s.insert(new string("1234"));
	s.insert(new string("abcd"));

	for (set<string *>::iterator it=s.begin();it!=s.end();++it) {
		cout<<*it<<endl;
		cout<<**it<<endl;
	}

	return 0;
}

可見上述輸出地址是升序的,但是字串完全亂序

解決辦法就是重新指定比較規則

bool comp(string *ps1, string *ps2)
{
	assert(ps1!=nullptr && ps2!=nullptr);
	return *ps1 < *ps2;
}

int main(int argc, char const *argv[])
{
	set<string *, bool(*)(string *, string *)> s(comp);
	s.insert(new string("qwer"));
	s.insert(new string("1234"));
	s.insert(new string("abcd"));

	for (set<string *>::iterator it=s.begin();it!=s.end();++it) {
		cout<<*it<<endl;
		cout<<**it<<endl;
	}

	return 0;
}

可見,重新指定比較函式後,輸出結果是按照字串的大小升序輸出

參考

《Effective STL》

歡迎大家評論交流,作者水平有限,如有錯誤,歡迎指出