zoukankan      html  css  js  c++  java
  • 第十六章 模板与泛型编程

    16.1

    知识点:当我们调用一个模板函数时,即向一个模板传递实参,编译器用此函数实参来推断模板实参,并将该模板实参(即实参的类型)绑定到模板参数(即T)。

    实例化:编译器用模板实参代替对应的模板参数来创建出一个新“实例”。譬如用int代替T,创建出一个新函数实例。

    16.2

    template <typename T>
    bool cmp(const T &a, const T &b)
    {
    	return a < b;
    }
    
    int main()
    {
    	cout << boolalpha << cmp('a', 'b') << " " << cmp(3, 2) << endl;
    	return 0; 
    }
    

      

    16.4

    #include <iostream>
    #include <vector>
    #include <list>
     
    using namespace std;
    
    template <typename T, typename V>
    bool find1(const T &beg, const T &end, const V &val)
    {
    	T it = beg;
    	while (it != end) {
    		if (*it == val)
    			return true;
    		++it;
    	}
    	return false;
    }
    
    int main()
    {
    	vector<int> vec{2, 1, 6, 4, 8, 7};
    	list<string> lst{"pine", "cake", "fine", "kzw"};
    	bool bl1 = find1(vec.begin(), vec.end(), 6);
    	bool bl2 = find1(lst.begin(), lst.end(), "tiple");
    	cout << boolalpha << bl1 << endl << bl2 << endl;;
    	return 0; 
    }
    

      

    16.5

    template<typename T, size_t N>
    void print(const T (&arr)[N])
    {
    	for (auto elem : arr)
    		cout << elem << endl;
    }
    
    int main()
    {
    	int a[] = {2, 3, 4, 5, 6};
    	char c[] = {'h', 'e', 'l', 'l', 'o'};
    	char c1[] = "hello";
    	print(a);
    	print(c);		//非类型参数N的值为5 
    	print(c1);		//非类型参数N的值为6 
    	return 0; 
    }
    

      

    16.6

    一个模板,它接受各种类型的数组,返回相应类型的指针。

    template<typename T, size_t N>
    const T* begin1(const T (&arr)[N])
    {
    	return arr;
    } 
    
    template<typename T, size_t N>
    const T* end1(const T (&arr)[N])
    {
    	return arr + N - 1;
    }
    

      

    16.7

    //way1 
    template<typename T, size_t N> constexpr 
    size_t size1(const T (&arr)[N])
    {
    	return N;
    }
    //way2 
    template<typename T> constexpr
    size_t size2(const T &arr)
    {
    	return sizeof(arr) / sizeof(arr[0]);
    }
    

      

    16.8

    有些类型并未定义<运算符,而大多数类型都定义了!=运算符(其中所有的标准库容器都有提供==与!=运算符)

    16.9

    知识点:一个模板就是一个创建类或函数的蓝图或者说公式。

    函数模板:一个公式,可用来生成针对特定类型的函数版本

    类模板:一个用来生成类的蓝图

    16.10

    当一个类模板被实例化时,编译器会重写类模板,将模板参数T的每个实例替换为给定的模板实参,生成一个类型确定的类

    16.11

    将List类和ListItem类定义为友元

    friend class ListItem<elemType>;

    16.12

    template <typename T> 
    class Blob {
    public:
    	//其他部分与书中相同 
    	const T& back() const;
    	const T& operator[](size_type) const;
    };
    
    template <typename T>
    const T& Blob<T>::back() const 
    {
    	check(0, "back on empty Blob");
    	return data->back();
    }
    
    template <typename T>
    const T& Blob<T>::operator[](size_type i) const
    {
    	check(i, "subscript out of range");
    	return (*data)[i];
    }

    16.13

    友好关系:限定在用相同类型实例化

    16.14

    template <unsigned H, unsigned W> 
    class Screen {
    public:
    	Screen() = default;
    private:
    	unsigned height = H;
    	unsigned weight = W;
    };
    
    int main()
    {
    	Screen<2, 3> s;
    	return 0; 
    }
    

      

    16.15

    template <unsigned H, unsigned W> 
    class Screen {
    public:
    	Screen() = default;
    	template <unsigned HH, unsigned WW>
    	friend istream& operator>>(istream &is, Screen<HH, WW> &scr);    //友元
    	template <unsigned HH, unsigned WW>
    	friend ostream& operator<<(ostream &os, const Screen<HH, WW> &scr);  //友元
    private:
    	unsigned height = H;
    	unsigned weight = W;
    };
    
    template <unsigned HH, unsigned WW>
    istream& operator>>(istream &is, Screen<HH, WW> &scr)
    {
    	//语句 
    }
    
    template <unsigned HH, unsigned WW>
    friend ostream& operator<<(ostream &os, const Screen<HH, WW> &scr)
    {
    	//语句 
    }
    
    int main()
    {
    	Screen<2, 3> s;
    	Screen<5, 6> s2;
    	cin >> s >> s2;
    	cout << s << endl;
    	return 0; 
    }
    

    友元的必要性:访问类的私有成员,并且因为该类型是模板,所以传入该类型作为形参时,相应的输入输出运算符也要作为函数模板。

    16.16

    template <typename T>
    class Vec {
    public:
    	Vec(): elements(nullptr), first_free(nullptr), cap(nullptr) { }
    	Vec(const Vec&);
    	Vec &operator=(const Vec&);
    	~Vec();
    	void push_back(const T&);
    	size_t size() const { return first_free - elements; }
    	size_t capacity() const { return cap - elements; }
    	T* begin() const { return elements; }
    	T* end() const { return first_free; }
    private:
    	T *elements;
    	T *first_free;
    	T *cap;
    	static allocator<T> alloc;
    	void chk_n_alloc(){ if (size() == capacity())	reallocate(); }
    	pair<T*, T*> alloc_n_copy(const T*, const T*);
    	void free();
    	void reallocate();
    };
    

      

    16.17

    没有不同。

    我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器改名字是一个类型,而只能通过关键字typename来实现。

    如,模板类型参数为T,我们在模板中有语句:T::size_type s,我们的本意是用域运算符访问T的类型成员size_type,然而C++默认域运算符访问的名字是static数据成员,故我们要达到目的,需定义如下语句:typename T::size_type s

    16.19

    #include <iostream>
    #include <vector>
    #include <list>
     
    using namespace std;
    
    template <typename T>
    void print(T &c)
    {
    	for (typename T::size_type i = 0; i != c.size(); ++i)
    		cout << c[i] << endl;
    }
    
    int main()
    {
    	vector<int> vec{0, 1, 2, 3};
    	print(vec);
    	return 0; 
    }
    

      

    16.20

    template <typename T>
    void print(T &c)
    {
    	for (auto it = c.begin(); it != c.end(); ++it)
    		cout << *it << endl;
    }
    

      

    16.21

    class DebugDelete {
    public:
    	template <typename T>
    	void operator()(const T *p) const
    	{
    		cout << "释放动态内存
    ";
    		delete p;
    	}	
    };
    
    int main()
    {
    	string *ps = new string("kzw");
    	DebugDelete d;
    	d(ps);			//等价于delete ps; 
    	return 0; 
    }
    

      

    16.22

    	shared_ptr<int> p(new int, DebugDelete());		//shared_ptr<T> p(new T, DebugDelete());

    16.23

    显然会在最后一个指向该对象的shared_ptr被销毁时调用

    16.24

    template <typename T>
    class Blob {
    public:
    	template <typename TT>
    	Blob(const TT&, const TT&);	
    };
    
    template <typename T>
    template <typename TT>
    Blob<T>::Blob(const TT &b, const TT &e): data(make_shared<vector<T>>(b, e)) { }
    

      

    16.25

    实例化声明,表明在程序其他位置有该实例化的一个非extern声明(定义),即本文件中不要对vector<string>类进行实例化

    实例化定义,表明该文件将包含vector<string>类的定义

    16.26

    不可以,我们显式实例化一个类模板时,会实例化该模板的所有成员。于是NoDefault也会被实例化,但它没有默认构造函数,故实例化失败。

    16.27

    a:没有实例化,只有在有数据时才会实例化
    b:没有实例化,引用并不会实例化,因为没有数值存在
    c:实例化出一个Stack<int>的实例
    d:没有实例化,指针不会实例化,指针包含的是地址
    e:实例化出一个Stack<char>的实例,因为函数接收到数据,而且是按值传递
    f:实例化出一个Stack<string>的实例

      

    16.28

    class DebugDelete {
    public:
    	template <typename T>
    	void operator()(const T *p) const
    	{
    		cout << "释放动态内存
    ";
    		delete p;
    	}	
    };
    
    int main()
    {
    	unique_ptr<int, DebugDelete> p1(new int,DebugDelete());
    	shared_ptr<int> p2(new int, DebugDelete());		//shared_ptr<T> p(new T, DebugDelete());
    	return 0; 
    }
    

      

    16.31

    删除器类型是unique_ptr类型的一部分,因此删除器成员的类型在编译时是知道的,从而删除器可以直接保存在unique_ptr对象中,而定义在类内的方法默认是内联的。

    16.32

    模板实参推断过程:从函数实参来确定模板实参的过程,编译器使用函数调用中的实参类型来寻找模板实参,用这些模板实参生成的函数版本与给定的函数调用最为匹配

    16.33

    将实参传递给带模板类型的函数形参时,能够自动应用的类型转换只有const转换及数组或函数到指针的转换。

    const转换:顶层const会被忽略;可以将一个非const对象的引用(或指针)传递给一个const的引用(或指针)形参

    数组或函数指针转换:函数形参不是引用类型,一个数组实参可以转换为一个指向其首元素的指针,而一个函数实参可以转换为一个该函数类型的指针

    16.34

    实参类型是const char*,而形参类型这是引用类型,故不合法

    这里的字符串的类型是根据长度判断的char [n],若长度不同,则类型也不同

    (a):不合法,两个实参类型不同,分别为char [3]、char [6](非const类型的引用或指针可以转换为const类型)

    (b):合法,两个实参类型相同,都是char [4]

    16.35

    (a)、(b)、(c)都合法

    (d):不合法,fcn只有一个类型的形参,故传递给该函数的实参必须具有相同的类型

    16.36

    (a):f1(int*, int*)

    (b):f2(int*, int*)

    (c):f1(const int*, const int*)

    (d):f2(const int*, const int*)

    (e):f1(const int*, const int*)   不合法,两个实参的类型不同(先判断类型是否相同,再考虑类型转换)

    (f ):f2(int*, const int*)

    16.37

    可以,只要显示指定模板类型参数,则函数实参就能进行正常的类型转换

    template <typename T>
    T Max(const T &a, const T &b)
    {
    	return a >= b ? a : b;
    }
    
    int main()
    {
    	auto c = Max(2, 3.0);	//错误
    	auto i = Max<int>(2, 3.0);	//3.0将转换为3 
    	return 0; 
    }
    

      

    16.38

    这样才能将内置指针转换为智能指针

    16.39

    显示指定模板类型,字符串字面常量可以是string,也可以是const char*

    template <typename T>
    int compare(const T &a, const T &b)
    {
    	if (a > b)	return 1;
    	else if (a < b)	return -1;
    	return 0;
    }
    
    int main()
    {
    	auto c = compare("kzw", "ly");	 //[Error] no matching function for call to 'compare(const char [4], const char [3])' 
    	auto i = compare<string>("kzw", "ly");
    	auto d = compare<const char *>("kzw", "ly");	//与目的相悖,比的是地址 
    	return 0; 
    }
    

      

    16.40

    合法,但是实参类型指向的对象必须定义了+运算符,并且可以与整数0相加(譬如string类型就不可以)

    decltype(*beg + 0)    <====>    decltype((*beg) + 0)

    16.41

    template <typename T>
    auto Sum(const T &a, const T &b) -> decltype(a + b)
    {
    	return a + b;
    }
    

      

    16.42

    (a):T的类型是int&,val的类型是int& &&

    (b):T的类型是const int&,val的类型是const int& &&

    (c):T的类型是int(i * ci为右值),val的类型是int &&

    16.43

    模板参数T是int&

    16.44

    函数参数声明为T:

    (a):T的类型是int

    (b):T的类型是const int    T的类型为int,因为函数参数声明不是T&,而是值传递,故const将被忽略

    (c):T的类型是int

    函数参数声明为const T&:

    (a):T的类型是int

    (b):T的类型是int

    (c):T的类型是int,一个const &参数可以绑定到一个右值

    16.45

    对一个字面常量调用g:如字面常量是int类型,则模板参数T是int

    对一个int类型的变量调用g:T为int&

    16.46

    将从长度为size()的(string的)vector中,从elem指向的string开始,逐个移动拷贝到dest指向的容器中

    std::move(*elem):将elem指向的string移动到某一位置

    16.47

    void f(int &&i, int &j)
    {
    	cout << i << " " << j << endl;
    }
    
    template <typename F, typename T1, typename T2>
    void flip(F f, T1 &&t1, T2 &&t2)
    {
    	f(std::forward<T2>(t2), std::forward<T1>(t1));
    }
    

      

    16.48

    template <typename T>
    string debug_rep(const T &s)
    {
    	ostringstream ret;
    	ret << t;
    	return ret.str();
    }
    
    template <typename T>
    string debug_rep(T *p)
    {
    	ostringstream ret;
    	ret << "pointer: " << p << " " << debug_rep(*p);
    	return ret.str();
    }
    
    string debug_rep(const string &s)
    {
    	return '"' + s + '"';
    }
    
    string debug_rep(char *p)
    {
    	return debug_rep(string(p));
    }
    
    string debug_rep(const char *p)
    {
    	return debug_rep(string(p));
    }
    

      

    16.49

    f(p):调用f(T),T相应的变为int*

    16.50

    #include <iostream>
     
    using namespace std;
    
    template<typename T> void f(T t) {  
        cout << "f(T): " << t << endl;  
    }  
    template<typename T> void f(const T *t) {  
        cout << "f(const T *): " << *t << endl;  
    }  
    template<typename T> void g(T t) {  
        cout << "g(T): " << t << endl;  
    }  
    template<typename T> void g(T *t) {  
        cout << "g(T *): " << *t << endl;  
    }  
    int main()  
    {  
        int i = 42, *p = &i;  
        const int ci = 0, *p2 = &ci;  
        g(42);          //g(T)
        g(p);           //g(T*) 
        g(ci);          //g(T)  
        g(p2);          //g(T*)  
        f(42);          //f(T)  
        f(p);           //f(T),p为指针,匹配f(T t),T为int* 
        f(ci);          //f(T)  
        f(p2);          //f(const T*)
        return 0;  
    }  
    

      

    16.52

    #include <iostream>
     
    using namespace std;
    
    template<typename T, typename...Args>
    void foo(const T &t, const Args &...rest)
    {
    	cout << sizeof...(Args) << endl;
    	cout << sizeof...(rest) << endl;
    }
    
    int main()  
    {  
        foo(2, 9.0, "kzw", 33);
        foo("ly", 33, 6.6);
        foo(4.3, 7);
        foo("520");
        return 0;  
    }  
    

      

    16.53

    #include <iostream>
     
    using namespace std;
    
    template <typename T>
    ostream& print(ostream &os, const T &t)
    {
    	os << t;
    	return os;
    }
    
    template <typename T, typename...Args>
    ostream& print(ostream &os, const T &t, const Args &...rest)
    {
    	os << t << "	";
    	return print(os, rest...);
    }
    
    int main()  
    {  
        print(cout, 2, "kzw", 520, 13.14);
        return 0;  
    }  
    

      

    16.54

    报错:[Error] cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'

    16.55

    在执行到函数参数包为空时,将无限递归调用自身

    16.56

    #include <iostream>
    #include <sstream>
     
    using namespace std;
    
    template <typename T>
    ostream& print(ostream &os, const T &t)
    {
    	os << t;
    	return os;
    }
    
    template <typename T, typename... Args>
    ostream& print(ostream &os, const T &t, const Args&... rest)
    {
    	os << t << "	";
    	return print(os, rest...);
    }
    
    template <typename T>
    string debug_rep(const T &s)
    {
    	ostringstream ret;
    	ret << s;
    	return ret.str();
    }
    
    template <typename T>
    string debug_rep(T *p)
    {
    	ostringstream ret;
    	ret << "pointer: " << p << " " << debug_rep(*p);
    	return ret.str();
    }
    
    string debug_rep(const string &s)
    {
    	return '"' + s + '"';
    }
    
    string debug_rep(char *p)
    {
    	return debug_rep(string(p));
    }
    
    string debug_rep(const char *p)
    {
    	return debug_rep(string(p));
    }
    
    template <typename... Args>
    ostream& errorMsg(ostream &os, const Args&... rest)
    {
    	return print(os, debug_rep(rest)...);
    }
    
    int main()  
    {  
    	errorMsg(cout, "ss", 2, "kzw");
        return 0;  
    }  
    

      

    16.57

    error_msg只可接受相同类型的实参

    16.58

    template <typename... Args>
    void StrVec::emplace_back(Args&&... args)
    {
    	chk_n_alloc();
    	alloc.construct(first_free++, std::forward<Args>(args)...);
    }
    

      

    16.59

    在construct调用中的模式会扩展为std::forward<string&>(s)

    16.60

    可接受可变参数模板,转发其参数初始化一个内存于内存空间,返回一个shared_ptr

    16.62

  • 相关阅读:
    sql server 分析
    3月5日总结
    sql server sql语句
    sql server数据类型char和nchar,varchar和nvarchar,text和ntext
    GIT 查看/修改用户名和邮箱地址
    Ubuntu安装LAMP环境(PHP5.6) 以及下载安装phpmyadmin
    Windows 10和Ubuntu 16.04双系统时间错误的调整
    php定界符<<<EOF讲解(转)
    matlab示例程序--Motion-Based Multiple Object Tracking--卡尔曼多目标跟踪程序--解读
    sa分析
  • 原文地址:https://www.cnblogs.com/xzxl/p/7783409.html
Copyright © 2011-2022 走看看