Day49、子类模板访问基类模板、模板型模板参数、嵌套模板的外部定义、“零”初始化、虚函数和多态、编译模型

本文探讨了在C++中子类模板如何正确访问基类模板中的成员,包括成员变量、成员函数和嵌套模板。通过示例代码解释了使用作用域限定符或`this`指针的必要性。接着介绍了模板型模板参数的概念,并展示了如何在模板类中使用模板型模板参数。此外,文章还讨论了嵌套模板的外部定义和‘零’初始化的重要性,确保模板中的变量在未显式初始化时能得到正确的默认值。最后,文章阐述了虚函数和多态的关系,以及模板函数不能声明为虚函数的原因。并总结了C++的几种编译模型及其优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5、子类模板访问基类模板

在子类模板中访问那些在基类模板中声明依赖于模板参数的符号,应该在它前面加上作用域限定符“::”或者显式使用this指针,否则编译器将试图在全局域中寻找该符号,引发错误。

举例:inherit.cpp

#include<iostream>

#include<cstdio>

using namespace std;

class A{

public:

   int m_var;

   void foo(void)const{}

   class X{};

};

class B:public A{

public:

   void bar(void)const{

       cout<</*A::*/m_var<<endl;

       /*A::*/foo();

       /*A::*/X x;

    }

};

template<typename T>

class C{

public:

   int m_var;

   void foo(void)const{}

   class X{};

   void exit (int status)const{

        cout<<"再见!"<<endl;

    }

};

template<typename T>

class D:public C<T>{

public:

   void bar(void)const{

//     cout<<C<T>::m_var<<endl;

       cout<<this->m_var<<endl;//和上一行等价

//     C<T>::foo();

       this->foo();

       typename C<T>::X x;

//     C<T>::exit(0);

       this->exit(0);

    }

};

int main(){

    Bb;

   b.bar();

   D<int> d;

   d.bar();

   return 0;

}

tarena@tarena-virtual-machine:~/day49$./a.out

134514601

-1218035724

再见!

6、模板型模板参数

类模板的模板参数如果结合的实参不是具体类型而是另一个模板,那么不能使用typename关键字声明该参数,而要写明其所结合的类模板实参的原型:

typename<模板形参表>class 模板型模板参数名

对于函数模板,GUN编译器允许为其传递模板型模板参数,但是c++标准并不支持

举例:

#include<iostream>

using namespace std;

template<typename T>

class Array{

public:

   void pushBack(T const& data){

       cout<<"向数组尾端压入数据"<<endl;

    }

   void popBack(void){

       cout<<"向数据尾端弹出数据"<<endl;

    }

};

template<typename T>

class List{

public:

   void pushBack(T const& data){

       cout<<"向链表尾端压入数据"<<endl;

    }

   void popBack(void){

       cout<<"向链表尾端弹出数据"<<endl;

    }

};

template<typename T,template<typename>classC>

class Stack{

public:

   void push(T const &data){

       m_c.pushBack(data);

    }

   void pop(void){

       m_c.popBack();

    }

private:

   C<T> m_c;

};

int main(){

   Stack<int,Array> sia;

   sia.push(100);

   sia.pop();

   Stack<int,List> sil;

   sil.push(100);

   sil.pop();

   return 0;

}

tarena@tarena-virtual-machine:~/day49$./a.out

向数组尾端压入数据

向数据尾端弹出数据

向链表尾端压入数据

向链表尾端弹出数据

 

7、嵌套模板的外部定义

如果将嵌套于一个类模板的内部模板放到包装模板的外部定义,需要按照作用域层次的顺序从外到内,从前到后依次使用独立的template字句声明其模板参数表。

举例:out.cpp

#include <iostream>

using namespace std;

/*

template<typename A>

class X {

public:

       template<typenameB>

       classY {

       public:

              template<typenameC>

              voidfoo (void) {}

       };

};

*/

template<typename A>

class X {

public:

       template<typenameB>

       classY;

};

template<typename A>

       template<typenameB>

class X<A>::Y {

public:

       template<typenameC>

       voidfoo (void);

};

template<typename A>

       template<typenameB>

              template<typenameC>

void X<A>::Y<B>::foo (void) {

}

int main (void) {

       return0;

}

8、“零”初始化

基本类型不存在缺省构造函数,违背显式初始化的局部变量和成员变量具有一个未定义的初值。如果希望模板函数或者模板类中所有参数化类型的变量,无论是基本类型还是类类型,都能以缺省方式被初始化,就必须显式进行缺省构造,即“零”初始化。

T var =T () ; // 局部变量

….:m_var() …. // 成员变量

举例:init.cpp

#include <iostream>

using namespace std;

template<typename T>

void foo (void) {

       Tvar = T ();

//     intvar = int ();

//     stringvar = string ();

//     Integervar = Integer ();

       cout<< '[' << var << ']' << endl;

}

template<typename T>

class Foo {

public:

       Foo(void) : m_var () {}

       Tm_var;

};

class Integer {

public:

       Integer(int arg = 0) : m_var (arg) {}

       friendostream& operator<< (ostream& os,

              Integerconst& i) {

              returnos << i.m_var;

       }

private:

       intm_var;

};

int main (void) {

       /*

       foo<int>();

       foo<string>();

       foo<Integer>();

       */

       Foo<int>fi;

       cout<< '[' << fi.m_var << ']'<< endl;

       Foo<string>fs;

       cout<< '[' << fs.m_var << ']' << endl;

       Foo<Integer>fn;

       cout<< '[' << fn.m_var  <<']' << endl;

       return0;

}

tarena@tarena-virtual-machine:~/day49/day03$./a.out

[0]

[]

[0]

9、虚函数和多态

1)类模板中可以声明虚函数,而且只要实例化该模板时所提供的类型实参不违背虚函数有效覆盖的条件,就可以形成多态。

2)由于模板函数的延迟编译要晚于类或者类模板中虚表的构建,因此模板函数不能同时又是虚函数。

举例:vf.cpp

#include <iostream>

using namespace std;

template<typename A, typename B>

class X {

public:

       //模板中的虚函数

       virtualA foo (B arg) const {

              cout<< "X::foo" << endl;

              returnA ();

       }

       //不可以声明虚模板函数

       /*

       template<typenameC>

       virtualvoid bar (void) const {}

       */

};

template<typename A, typename B,

       typenameC, typename D>

class Y : public X<C, D> {

public:

       Afoo (B arg) const {

              cout<< "Y::foo" << endl;

              returnA ();

       }

};

int main (void) {

       Y<int,double, int, double> y1;

       X<int,double>& x1 = y1;

       x1.foo(1.2);

       Y<int,double, int, char> y2;

       X<int,char>& x2 = y2;

       x2.foo('A');

       /*

       Y<int,double, char, double> y3;

       X<char,double>& x3 = y3;

       x3.foo(1.2); */

       return0;

}

tarena@tarena-virtual-machine:~/day49/day03$./a.out

Y::foo

X::foo

列出不能被声明为虚函数的函数:全局函数、静态成员函数、构造函数、模板型成员函数

 

一、编译模型

1、 单一模型:将函数、类和模板的声明、实现和使用放在同一个编译单元中

优点:一目了然、构建方便

缺点:难以维护,难以复用

2、 分离模型:将函数、类和模板的声明、实现和使用放在不同的源代码文件中。

优点:易于维护,易于复用、便于协作开发

缺点:需要编写专门的构建脚本----Makefile

致命问题:编译模板实现代码时编译器看不到对模板的使用,因此生成的目标模块中没有关于该模板的二进制指令,最终导致链接失败。

举例:Mkdir div

3、 包含模型:在模板声明文件的尾部包含模板实现文件,保证所有使用该模板的代码在头文件扩展以后都实际包含了该模板的声明和实现,保证延迟编译的顺利进行,链接成功。

优点:解决了分离模型的致命问题-----链接失败

缺点:模板的实现代码必须公开、延长编译时间

举例:mkdir inc

4、 实例模型:在模板的实现中包含对该模板的显示实例化代码,旨在迫使编译器在看到对模板的使用之前,先做二次编译,保证链接通过。

优点:模板的实现代码不必公开,编译时间不受影响

缺点:实例化类型有限,无法实现绝对通用

举例:mkdir  ins

5、 导出模型:通过export关键字将模板声明为导出,编译器会将该模板一次编译之后形成的内部表示缓存到其目标文件中(.o)中,在链接阶段结合使用该模板的代码所提供的类型实参,完成二次编译。

优点:代码分离,实现不公开、编译速度快、类型通用

缺点:绝大多数编译器并不支持该模板

举例:mkdir exp

 

二、容器、迭代器和泛型算法

容器:基于类模板语法实现泛型数据结构。

迭代器:基于指针语法实现面向不同容器的统一访问方式。

泛型算法:基于函数模板语法实现泛型常用算法。

 

双向线性链表容器

正向顺序可写迭代器

线性查找算法

 

vi.list.cpp

#include <iostream>

#include <cstring>

#include <stdexcept>

using namespace std;

// 双向线性链表

template<typename T>

class List {

public:

   // 构造、析构、深拷贝构造、拷贝赋值

   List (void) :m_head (NULL),

       m_tail (NULL) {}

   ~List (void) {

       clear ();

    }

   List (List const& list) :m_head (NULL),

       m_tail (NULL) {

       for (Node* node = list.m_head;

            node;node = node->m_next)

            push_back (node->m_data);

    }

   List& operator= (List const& list) {

       if (&list != this) {

           List temp = list;  // 拷贝构造

           swap (m_head,temp.m_head);

           swap (m_tail,temp.m_tail);

       }

       return *this;

    }

   // 获取首元素

   T& front (void) {

       if (empty ())

           throw underflow_error ("链表下溢!");

       return m_head->m_data;

    }

    Tconst& front (void) const {

       return const_cast<List*> (this)->

           front ();

    }

   // 向首部压入

   void push_front (T const& data) {

       m_head = new Node (data,NULL,

           m_head);

       if (m_head->m_next)

           m_head->m_next->m_prev =m_head;

       else

           m_tail = m_head;

    }

   // 从首部弹出

   void pop_front (void) {

       if (empty ())

           throw underflow_error (

                "链表下溢!");

       Node* next = m_head->m_next;

       delete m_head;

       m_head = next;

       if (m_head)

           m_head->m_prev = NULL;

       else

           m_tail = NULL;

    }

   // 获取尾元素

   T& back (void) {

       if (empty ())

           throw underflow_error ("链表下溢!");

       return m_tail->m_data;

    }

    Tconst& back (void) const {

       return const_cast<List*> (this)->

           back ();  

    }

   // 向尾部压入

   void push_back (T const& data) {

       m_tail = new Node (data,m_tail);

       if (m_tail->m_prev)

           m_tail->m_prev->m_next = m_tail;

       else

           m_head = m_tail;

    }

   // 从尾部弹出

   void pop_back (void){

       if (empty ())

           throw underflow_error ("链表下溢!");

       Node* prev = m_tail->m_prev;

       delete m_tail;

       m_tail = prev;

       if (m_tail)

           m_tail->m_next = NULL;

       else

           m_head = NULL;

    }

   // 删除所有匹配元素

   void remove (T const& data) {

       for (Node* node = m_head,*next;

           node;node = node = next) {

           next = node->m_next;

           if (node->m_data == data) {

                //若不是头结点

                if (node->m_prev)

                    //前结点的后指针指向下一个结点

                    node->m_prev->m_next= 

                    node->m_next;

                //若是头结点

                else

                    m_head = node->m_next;

                //若不是尾结点

                if (node->m_next)

                    //后结点的前指针指向下一个结点

                    node->m_next->m_prev=

                    node->m_prev;

                //若是尾结点

                else

                    m_tail = node->m_prev;

                delete node;

           }

       }

    }

   // 清空

   void clear (void) {

       while (! empty ())

           pop_back ();

    }

   // 判空

   bool empty (void) const {

       return ! m_head && ! m_tail;

    }

   // 大小(元素个数)

   size_t size (void) const {

       size_t nodes = 0;

       for (Node* node = m_head;node;

           node = node->m_next)

           ++nodes;

       return nodes;

    }

   // 插入输出流

   friend ostream& operator<< (ostream& os,

       List const& list) {

       for (Node* node = list.m_head;node;

           node = node->m_next)

           os << *node;

       return os;

    }

private:

   // 节点

   class Node {

   public:

        // 构造器

       Node (T const& data,

           Node* prev = NULL,

           Node* next = NULL):

           m_data (data),m_prev (prev),

           m_next (next) {}

       // 插入输出流

       friend ostream& operator<< (

           ostream& os,Node const& node) {

           return os << '(' << node.m_data << ')';

       }

       T     m_data; // 数据

       Node* m_prev; // 前指针

       Node* m_next; // 后指针

   };

   Node* m_head; // 头指针

   Node* m_tail; // 尾指针

};

// 测试用例

void test1 (void) {

   List<int> li;

   li.push_front (30);

   li.push_front (20);

   li.push_front (15);

   cout << li << endl; // 15 20 30

   li.front () -= 5;

   cout << li << endl; // 10 20 30

   List<int> const& cr = li;

// ++cr.front ();

   cout << cr.front () << endl;

   li.pop_front (); // 10

   cout << li << endl; // 20 30

   li.push_back (40);

   li.push_back (50);

   li.push_back (55);

   cout << li << endl; // 20 30 40 50 55

   li.back () += 5;

   cout << li << endl; // 20 30 40 50 60

   List<int> const* cp = &li;

// cp->back ()--;

   cout << cp->back () << endl; // 60

   li.pop_back ();

   cout << li << endl; // 20 30 40 50

   li.push_front (30);

   li.push_front (30);

   li.push_back (30);

   li.push_back (30);

   cout << li << endl; // 30 30 20 30 40 50 30 30

   li.remove (30);

   cout << li << endl; // 20 40 50

   cout << boolalpha << li.empty () << ' '

       << li.size () << endl; // false 3

   li.clear ();

   cout << li.empty () << ' '

       << li.size () << endl; // true 0

}

void test2 (void) {

   List<int> l1;

   l1.push_back (100);

   l1.push_back (200);

   l1.push_back (300);

   l1.push_back (400);

   l1.push_back (500);

   List <int> l2 = l1; // 拷贝构造

   cout << "l1: " << l1 << endl;

   cout << "l2: " << l2 << endl;

   // 验证是否为深拷贝

   l1.pop_front (); // 200 300 400 500

   l2.pop_back (); //  100 200 300400

   cout << "l1: " << l1 << endl;

   cout << "l2: " << l2 << endl;

   l2 = l1; // 拷贝赋值

   cout << "l1: " << l1 << endl;

   cout << "l2: " << l2 << endl;

    l1.push_front (100);

   l2.push_back (600);

   cout << "l1: " << l1 << endl;

   cout << "l2: " << l2 << endl;

}

// 进程入口

int main(void)

{

// test1 ();

   test2 ();

   return 0;

}

tarena@tarena-virtual-machine:~/day49/div$./a.out

l1: (100)(200)(300)(400)(500)

l2: (100)(200)(300)(400)(500)

l1: (200)(300)(400)(500)

l2: (100)(200)(300)(400)

l1: (200)(300)(400)(500)

l2: (200)(300)(400)(500)

l1: (100)(200)(300)(400)(500)

l2: (200)(300)(400)(500)(600)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值