第一章 STL概述
可能令你困惑的C++语法
1.类里面的static成员
当使用模板类初始化多个实例时,这些实例共享这个模板(同类型)类的static成员。
#include <iostream>
template<typename T>
class myClass
{
public:
static int i1_;
static float f1_;
};
// int type template
int myClass<int>::i1_ = 1;
float myClass<int>::f1_ = 1.1f;
// float type template
int myClass<float>::i1_ = 2;
float myClass<float>::f1_ = 2.2f;
int main()
{
myClass<int> ic1_, ic2_;
myClass<float> fc1_, fc2_;
std::cout
<< ic1_.i1_ << ' '
<< ic1_.f1_ << ' '
<< ic2_.i1_ << ' '
<< ic2_.f1_ << ' '
<< fc1_.i1_ << ' '
<< fc1_.f1_ << ' '
<< fc2_.i1_ << ' '
<< fc2_.f1_ << ' ';
return 0;
}
C++综上可知,当对ic1_实例的i1_成员进行操作时,i2_对应的成员也会发生变化, 因为它们使用的是同一个模板类myClass<int>
,同理,fc1_和fc2_也如此。
2.模板类的特殊设计
初始化实例时,编译器会根据提供的指针的类型进行匹配。
#include <iostream>
// generalized
template<class T, class O>
struct tc
{
tc() { std::cout << "I, O" << '\n'; }
};
// specialized
template<class T>
struct tc<T*, T*>
{
tc() { std::cout << "T*, T*" << '\n'; }
};
// specialized
template<class T>
struct tc<const T*, T*>
{
tc() { std::cout << "const T*, T*" << '\n'; }
};
int main()
{
tc<int, char> obj1;
tc<int*, int*> obj2;
tc<const int*, int*> obj3;
tc<const int*, const int*> obj4;
tc<int*, const int*> obj5;
return 0;
}
C++输出结果:
若匹配不到,则会自动选择默认模板类。
3.模板类中可再存在模板成员
#include <iostream>
class alloc{};
template<class T, class Alloc = alloc>
class vector{ // template class
public:
typedef T value_type; // T -> value_type
typedef value_type* iterator; // value_type* -> iterator
template<class I> // template member function
void insert(iterator position, I first, I last){ std:: cout << "insert()" << '\n'; }
};
int main(){
int ia[5] = {0, 1, 2, 3, 4};
vector<int> x;
vector<int>::iterator ite = nullptr;
x.insert(ite, ia, ia + 5);
return 0;
}
C++4.template 参数可以根据前一个 template 参数而设定默认值
#include <iostream>
#include <cstddef>
class alloc {};
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque{
public:
deque() { std::cout << "deque()" << '\n'; }
};
// 根据前一个参数值T, 设定下一个参数Sequence的默认值为deque<T>
template<class T, class Sequence = deque<T>>
class stack {
public: stack() { std::cout << "stack()" << '\n'; }
private: Sequence c;
};
int main() {
stack<int> x;
return 0;
}
C++输出结果:
由输出结果可以看出,在初始化stack实例时,stack内的Sequence会先根据模板内的参数初始化一个deque实例,再实例化stack。
综上可知,当初始化一个模板类时,模板类中的模板中若有其他模板类,会优先初始其他化模板类对应的实例。
示例1,如下所示:
#include <iostream>
#include <cstddef>
class alloc {};
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque{
public:
deque() { std::cout << "deque()" << '\n'; }
};
// 先初始化deque的实例
template<class T, class Sequence = deque<T>>
class stack {
public: stack() { std::cout << "stack()" << '\n'; }
private: Sequence c;
};
// 先初始化stack的实例
template<class T, class Test = stack<T>>
class test
{
public:
test() { std::cout << "test()" << '\n'; }
private:
Test t;
};
int main() {
test<int> t_;
return 0;
}
C++输出结果:
示例2,如下所示
#include <iostream>
#include <cstddef>
class alloc {};
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque{
public:
deque() { std::cout << "deque()" << '\n'; }
};
template<class T, class Test = deque<T>>
class test
{
public:
test() { std::cout << "test()" << '\n'; }
private:
Test t;
};
template<class T, class Sequence = deque<T>, class test_ = test<T>>
class stack {
public: stack() { std::cout << "stack()" << '\n'; }
private: Sequence c; test_ _t;
};
int main() {
stack<int> t_;
return 0;
}
C++结果如下:
过程推导:
- 实例化deque -> deque()
- 实例化test_, 但实例化test_前需实例化deque -> deque()
- 实例化test_完成 -> test()
- 实例化stack完成 -> stack()
5.模板类可拥有Non-type的模板参数
#include <iostream>
#include <cstddef>
class alloc {};
inline size_t __deque_buf_size(size_t n, size_t sz) {
return n != 0 ? n : (sz < 512) ? size_t(512 / sz) : size_t(1);
}
template<class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
static size_t buffer_size() { return __deque_buf_size(BufSiz, sizeof(T)); }
};
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public: typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
};
int main() {
std::cout << deque<int>::iterator::buffer_size() << '\n';
std::cout << deque<int, alloc, 64>::iterator::buffer_size() << '\n';
return 0;
}
C++在类deque
中的成员iterator
初始化时利用模板中的参数BufSiz
是一个value
而不是一个type
。
由此可知,模板接受非类型non-type
的参数。
6.Bound Friend Template
类模板的某个具体实例(instantiation)与其友元函数模板的具体实例(instantiation)一一对应。
#include <iostream>
#include <cstddef>
class alloc {};
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque{
public: deque() { std::cout << "deque()" << '\n'; }
};
template<class T, class Sequence>
class stack;
template<class T, class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y);
template<class T, class Sequence>
bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y);
template<class T, class Sequence = deque<T>>
class stack{
// style 1
friend bool operator== <T> (const stack<T>& , const stack<T>& );
friend bool operator< <T> (const stack<T>& , const stack<T>& );
// style 2
friend bool operator== <T> (const stack&, const stack&);
friend bool operator< <T> (const stack&, const stack&);
// style 3
friend bool operator== <> (const stack&, const stack&);
friend bool operator< <> (const stack&, const stack&);
// invalid style
//friend bool operator== (const stack&, const stack&);
//friend bool operator< (const stack&, const stack&);
public: stack() { std::cout << "stack()" << '\n'; }
private: Sequence c;
};
template<class T, class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y){
std::cout << "operator==()" << '\t';
return true;
}
template<class T, class Sequence>
bool operator< (const stack<T, Sequence>& x, const stack<T, Sequence>& y){
std::cout << "operator<()" << '\t';
return true;
}
int main(){
stack<int> x;
stack<int> y;
std::cout << (x == y) << '\n';
std::cout << (x < y) << '\n';
//stack<int> y_;
// invalid
// std::cout << (x == y_) < '\n'; // no match for this
// std::cout << (x < y_) << '\n'; // no match for this
return 0;
}
C++一般步骤:在模板类内用友元函数声明要重载的运算符,(必须)再对声明的重载友元函数进行实现。
输出结果:
7.类模板显式声明
#include <iostream>
#define __STL_TEMPLATE_NULL template<>
template<class Key>
struct hash{
void operator() () { std::cout << "hash<T>" << '\n'; }
};
// explicit specialization
__STL_TEMPLATE_NULL struct hash<char>{
void operator() () { std::cout << "hash<char>" << '\n'; }
};
__STL_TEMPLATE_NULL struct hash<unsigned char>{
void operator() () { std::cout << "hash<unsigned char>" << '\n'; }
};
int main(){
hash<long> t1;
hash<char> t2;
hash<unsigned char> t3;
t1();
t2();
t3();
return 0;
}
C++输出结果:
hash<long> -> hash<T> ∵hash<long>并没有被显式声明,故调用默认模板
hash<char> -> hash<char> ∵hash<char>已经被显式声明,故调用特定模板
hash<unsigned char> -> hash<unsigned char> 原因同上
C++通过这样的做法,可以自定义不同类型模板类的行为。
8.临时对象的产生与运用
#include <vector>
#include <iostream>
#include <algorithm>
template<typename T>
class print{
public: void operator() (const T& elem) { std::cout << elem << ' '; }
};
int main(){
int ia[6] = {0, 1, 2, 3, 4, 5, 6};
std::vector<int> iv(ia, ia + 5);
// print<int> is an unnamed object but not a invoking operation
std::for_each(iv.begin(), iv.end(), print<int>());
return 0;
}
C++临时对象即无名对象(unnamed objects)。 实现方法
对()符号进行重载: void operator() (const T& elem) { std::cout << elem << ' '; }
不需声明指定对象名称,直接通过()进行调用std::for_each(iv.begin(), iv.end(), print<int>());
此临时对象的生命周期
状态 | 动作 |
---|---|
开始 | for_each()(或其他函数)第一次调用print<int>()(临时对象) |
结束 | for_each()结束时 |
9.静态常量成员在class内部直接初始化
#include <iostream>
template<typename T>
class testClass{
public: expedient
static const int _datai = 5;
static const long _datal = 3l;
static const char _datac = 'c';
};
int main(){
std::cout << testClass<int>::_datai << '\n';
std::cout << testClass<long>::_datal << '\n';
std::cout << testClass<char>::_datac << '\n;
return 0;
}
C++10.increment/decrement/dereference 操作符
此类操作符可用于迭代器(iterator)的指针的移动或取值(dereference)。
移动操作可分为前置(prefix)和后置(postfix。
#include <iostream>
class INT{
friend std::ostream& operator<<(std::ostream& os, const INT& i);
public: INT(int i): m_i(i) {};
// prefix: increment and then fetch
INT& operator++(){
++(this->m_i);
return *this;
}
// postfix: fetch and then increment
const INT operator++(int){
INT temp = *this;
++(*this);
return temp;
}
// prefix: minus and then fetch
INT& operator--(){
--(this->m_i);
return *this;
}
// postfix: fetch and then increment
const INT operator--(int){
INT temp = *this;
--(*this);
return temp;
}
// deference
int& operator*() const{
std::cout << "dereference" << '\n';
return (int&)m_i;
}
private: int m_i;
};
std::ostream& operator<<(std::ostream& os, const INT& i){
os << '[' << i.m_i << ']';
return os;
}
int main(){
INT I(5);
std::cout << I++ << '\n';
std::cout << ++I << '\n';
std::cout << I-- << '\n';
std::cout << --I << '\n';
std::cout << *I << '\n';
return 0;
}
C++输出结果:
分析:
- prefix increment :即前置++,类似地像
++i
这样的,优先对i进行操作后再使用。
// prefix: increment and then fetch
INT& operator++(){
++(this->m_i);
return *this;
}
C++- postfix increment :即后置++,类似地像
i++
这样的,优先使用i后,再对其进行操作。
// postfix: fetch and then increment
const INT operator++(int){
INT temp = *this; // 声明一个临时对象temp用于存储要操作的目标对象,即还没被操作的目标对象
++(*this);
return temp; // 返回没有被操作的目标对象
}
C++- dereference :即解引用,返回目标对象的对应类型的地址。
// deference
int& operator*() const{
std::cout << "dereference" << '\n';
return (int&)m_i;
}
C++11.前闭后开区间表示法 [ )
template<class Iterator, class T>
InputIterator find(Iterator first, Iterator Last, const T& value){
while(first != last & *first != value) ++first;
return first;
}
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f){
for(; first != last; ++first) f(*first)
return f;
}
C++前闭后开即 [first, last), 让指针从first开始 而达不到last(last的前一个位置 => last - 1)。
12.function call操作符(operator())
#include <iostream>
template<class T>
struct plus{
T operator() (const T& x, const T& y) const { return x + y; }
};
template<class T>
struct minus{
T operator() (const T& x, const T& y) const { return x - y; }
};
int main(){
plus<int> plusobj; // functor object of plus
minus<int> minusobj; // functor object of minus
// invoke the functor by the corresponding instances
std::cout << plusobj(5, 10) << '\n';
std::cout << minusobj(10, 5) << '\n';
// create the temporary object as the functor
std::cout << plus<int>()(30, 20) << '\n';
std::cout << minus<int>()(210, 10) << '\n';
return 0;
}
C++此处对各模板类的 () 进行了重载,使之成为了一个仿函数(functor)