类型参数+非类型模版参数
//类型参数+非类型模版参数
template<class T,int N>
class Array
{
public:
Array() {}
private:
T _a[N];
};
int main()
{
Array<int, 100> a1;
return 0;
}
注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。
模版的特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结 果
template<class T>
bool IsEqual(T& left, T& right)
{
return left == right;
}
//特化
template<>
bool IsEqual<char*>(char*& left, char*& right)
{
return strcmp(left, right) == 0;
}
int main()
{
//Array<int, 100> a1;
int a = 0, b = 1;
cout << IsEqual(a, b) << endl;
const char* p1 = "hello";
const char* p2 = "world";
cout << IsEqual(p1, p2) << endl;
return 0;
}
全特化
全部参数都特化
template<>
bool IsEqual<char*>(char*& left, char*& right)
{
return strcmp(left, right) == 0;
}
偏特化
可以是特化部分参数/或者对参数进行进一步的限制
template<class T2>
class Date<int T2>
{};
template<class T1,class T2>
class Data<T1*,T2*>
{};
模板分离编译
什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链 接起来形成单一的可执行文件的过程称为分离编译模式。
【Linux】编译器-gcc/g++的使用(预处理、编译、汇编、连接)
【Linux】编译器-gcc/g++的使用(预处理、编译、汇编、连接)_g++ 预编译-CSDN博客https://blog.csdn.net/dhgiuyawhiudwqha/article/details/136849183
同样是分离编译,普通函数/类可以,函数模版/类模版不行。
模版定义时不会实例化,使用时才会
Func.o中有定义但是却不知道要实例化为什么类型
test.o中有声明但是没有定义 故链接时会发生报错
Func.o中有符号表,表中有函数地址。
链接会将目标文件结合到一起,有声明没有定义即可编译通过,链接时在Func.o中找函数地址,函数地址存在才能编译通过。
显示实例化
#include"Func.h"
void F1()
{
cout << "F1()" << endl;
}
template<class T>
void F2(const T& x)
{
cout << "void F2(const T&x)" << endl;
}
//显示实例化
template
void F2<int>(const int& x);
解决此问题的另一种方法比较粗暴就是直接将其全放入.h文件中。
继承
继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "peter"; // 姓名
int _age = 18; // 年龄
};
// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Student和
Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可以看到变量的复用。
调用Print可以看到成员函数的复用。
class Student : public Person
{
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
int main()
{
Student s;
Teacher t;
s.Print();
t.Print();
return 0;
}
继承定义
类中的私有和保护在当前类没差别。
在继承后的子类中有差别,private的成员在子类中不可见。
总结:
1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是 被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能 访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类 的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。 4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的 写出继承方式。
5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用 protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中 扩展维护性不强。
子类和父类之间的赋值兼容规则
1.子类对象可以赋值给父类对象/指针/引用(切片)
当父类和子类同时有同名成员时,子类的成员隐藏了父类的成员。(重定义)
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A
{
public:
void fun()
{
cout << "func()" << endl;
}
};
class B : public A
{
public:
void fun(int i)
{
A::fun();
cout << "func(int i)->" <<i<<endl;
}
};
void Test()
{
B b;
b.fun(10);
};