C++ 代码重用
最最后一遍关于C++类与对象的文章
类与对象的内容实在是太多了,所以这里还得新开一篇文章来讲
包含对象成员的类
我们可以在我们想要创建的类中使用其他类作为数据成员
比如我们要创建一个Student类,我们使用string类来保存学生的名字,使用valarray类来动态分配关于学生的成绩的数组
首先是Student类的声明:
class Student
{
private:
string name;
typedef valarray<double> Array; //使用ypedef简化下面的初始化
Array scores; //初始化一个double类型的动态数组
ostream &arr_out(ostream &os)const;
public:
Student():name("no student"),scores(){}
Student(const string &s):name(s),scores(){}
explicit Student(int n):name("nully"), scores(n){} //关闭隐式转换
Student(const string &s, const Array &a):name(s), scores(a){}
Student(const char *str, const double *pd, int n):name(str), scores(pd, n){}
~Student(){}
double Average() const;
const string &Name()const;
double &operator[](int i);
double operator[](int i) const;
friend istream & operator>>(istream &is, Student&stu);
friend istream & getline(istream &is, Student &stu);
friend ostream & operator<<(ostream &os, Student &stu);
};
上面的构造函数全部使用初始化成员列表方法
然后是具体方法的编写:
double Student::Average() const
{
if(scores.size()>0)
return scores.sum()/scores.size();
else
return 0;
}
const string & Student::Name()const
{
return name;
}
double &Student::operator[](int i)
{
return scores[i];
}
double Student::operator[](int i) const
{
return scores[i];
}
ostream & Student::arr_out(ostream&os) const
{
int i;
int li = scores.size();
if(li>0)
{
for(i=0;i<li;i++)
{
os<<scores[i]<<" ";
if(i%5==4)
os<<endl;
}
if(i%5!=0)
os<<endl;
}
else
os << "empty array!";
return os;
}
istream & operator>>(istream&is, Student &stu)
{
is >> stu.name;
return is;
}
istream & getline(istream&is, Student &stu)
{
getline(is, stu.name);
return is;
}
ostream &operator<<(ostream &os, Student &stu)
{
os<<"Scores for "<<stu.name<<":\n";
stu.arr_out(os);
return os;
}
然后是main函数调用测试:
void set(Student &sa, int n);
const int pup = 3;
const int qui = 5;
int main()
{
Student arr[pup] =
{Student(qui),Student(qui), Student(qui)};
int i;
for(i = 0;i<pup;i++)
set(arr[i], qui);
cout << "\nStudent list:\n";
for(i=0;i<pup;i++)
cout<<arr[i].Name()<<endl;
cout << "\nresults:\n";
for(i=0;i<pup;i++)
{
cout <<endl<<arr[i];
cout <<"average: "<<arr[i].Average()<<endl;
}
return 0;
}
void set(Student &sa, int n)
{
cout <<"please enter the student's name: ";
getline(cin, sa);
cout <<"please enter "<<n<<" quiz scores:\n";
for(int i=0;i<n;i++)
cin >> sa[i];
while(cin.get()!='\n')
continue;
}
输出结果:
please enter the student's name: Mike Young
please enter 5 quiz scores:
60 60 60 60 60
please enter the student's name: Eddie Smith
please enter 5 quiz scores:
95 95 95 95 95
please enter the student's name: Bob Frank
please enter 5 quiz scores:
100 100 100 100 100
Student list:
Mike Young
Eddie Smith
Bob Frank
results:
Scores for Mike Young:
60 60 60 60 60
average: 60
Scores for Eddie Smith:
95 95 95 95 95
average: 95
Scores for Bob Frank:
100 100 100 100 100
average: 100
里面的函数大家可以自己动手试试哟!
私有继承
使用公有继承时,基类的公有方法将成为派生类的公有方法
使用私有继承时,基类的公有方法将成为派生类的私有方法
初始化成员
我们使用私有继承来实现上面的Student类
Student类从两个类派生而来,所以我们应该这样声明:
class Student : private string,private valarray<double>
{
public:
……
}
//我们不再需要私有成员,两个基类提供了所需的数据成员
这种使用多个基类的继承被称为多重继承
由于没有创建成员名,所以我们使用列表初始化列表句法来初始化成员时,我们应该使用类名而不是成员名:
原来的构造函数:
Student(const char *str, const double *pd, int n)
:name(str), scores(pd, n){}
现在的构造函数
Student(const char*str, const double*pd, int n)
:string(str), Array(pd, n){}
访问基类的方法
使用私有继承时,只能在派生类的方法中使用基类的方法。
私有继承使得能够使用类名和作用域解析操作符(::)来调用基类的方法
比如这个函数:
double Student::Average() const
{
if(scores.size()>0)
return scores.sum()/scores.size();
else
return 0;
}
在私有继承中应该这么写:
double Student::Average() const
{
if(Array::.size()>0)
return Array::.sum()/Array::.size();
else
return 0;
}
访问基类对象
我们使用强制类型转换来访问基类对象
比如这个函数:
const string & Student::Name()const
{
return name;
}
我们需要返回派生类中的string对象,我们做如下强制转换:
const string & Student::Name()const
{
return (const string &)*this;
}
将Student对象转换为string对象,再使用this指针返回调用方法的对象
保护继承
将声明时的private换成protected便是保护继承
使用保护继承时,基类的公有成员和保护成员都变成派生类的保护成员,基类的接口在派生类中可用
多重继承
也就是从两个或多个基类中派生出一个派生类
这主要会引发两个问题:
1. 从两个不同的基类继承同名方法
2. 从两个或多个相关基类那里继承同一个类的多个实例
我们需要引入虚基类来解决这些问题
我们还需要引入新的构造函数规则
由于难度较大,而且一般可以使用多重继承的例子,我们可以使用包含对象成员的类的方法完成,所以这里不再赘述,感兴趣的朋友可以自行寻找资料学习哟!
类模板
类模板的基本使用方法
我们知道使用函数模板,是可以大大减少我们编写代码的量,那么类是否也有类似的功能呢?这里C++同样提供了类模板,来生成通用的类声明
模板提供参数化类型,能够将类型名作为参数传递来建立类或者函数
基本思想跟函数模板一致,如果对这里的类模板不太理解可以先看看我的另一篇文章:
同样我们需要一个声明表示该类为类模板:
template <class Type>
// 这里的class并不意味着Type是一个具体的类
// 这里的Type可以换成你自己想用的通用类型,比如T,B
这里同样使用栈的例子来实现类模板,因为栈的类型不同,类中的数组成员会不同,所以我们使用类模板来根据实例来创建不同类型的栈
首先我们看类模板的声明:
template <class Type>
class Stack //类模板
{
private:
Type items[10]; //使用Type制造模板
int top;
public:
Stack(); //构造函数
void isempty_full();//判断空满
bool push(const Type& item);//压栈
Type pop();//出栈并返回相应类型的值
};
然后和函数模板类似,在每个成员函数的具体实现前加上:
template <class Type>
注意:和你自己在类模板上面定义的声明保持一致!
然后是成员函数的编写:
template<class Type>
Stack<Type>::Stack()
{
top = 0;
}
template<class Type>
void Stack<Type>::isempty_full()
{
if(top==0)
cout << "Stack is empty!\n";
if(top==10)
cout << "Stack is full!\n";
if(top>0&&top<10)
cout << "Stack is available!\n";
}
template<class Type>
bool Stack<Type>::push(const Type &item)
{
if(top<10)
{
items[top++] = item;
cout << item << " has been pushed into Stack!\n";
return true;
}
else
return false;
}
template<class Type>
Type Stack<Type>::pop() //注意这个函数,可以说是嵌套了函数模板
{
Type item;
if(top>0)
{
item = items[--top];
cout << item << " has removed from Stack!\n";
return item;
}
else
return item;
}
基本的栈的操作和初始化定义,所以就没有写详细注释啦!
最后是main函数测试我们编写的类模板:
int main()
{
Stack<int> sint; //实例化两个类模板
Stack<char> schar;
cout << "Int Stack:\n";
sint.push(2);
sint.isempty_full();
int re;
re = sint.pop();
cout << "value removed from Stack is : " << re <<endl;
cout << "\n";
cout << "Char Stack:\n";
schar.isempty_full();
char a = 'A';
schar.push(a);
char c;
c = schar.pop();
cout << "value removed from Stack is : "<<c<<endl;
return 0;
}
也是很简单的例子,不多加注释,配合着输出结果大家就可以理解啦!
输出结果:
Int Stack:
2 has been pushed into Stack!
Stack is available!
2 has removed from Stack!
value removed from Stack is : 2
Char Stack:
Stack is empty!
A has been pushed into Stack!
A has removed from Stack!
value removed from Stack is : A
类模板的几个使用技巧
递归使用模板
我们可以这样使用模板:
template <class Type, int n> //允许添加其他附加信息
Type<Type<int, 5>, 10> sint;
//实例化一个 int sint [10][5]
//注意行列的书写顺序
使用多个类型参数
我们可以让一个模板中出现多个类型参数:
template <class Type1, class Type2>
给类型参数提供默认值
template <class Type1, class Type2=int>
成员模板
模板可以作为结构,类或者模板类的成员
我们可以在类模板中嵌套另外的模板类或者模板函数,比如下面这个类模板的声明:
template <typename T>
class beta
{
private:
template <typename V>
class hold
{
private:
V val;
public:
hold(V v=0):val(v){}
void show()const{cout << val <<endl;}
V value()const {return val;}
};
hold<T> q;
hold<int> n;
public:
beta(T t, int i):q(t),n(i){}
template<typename U>
U add(U u,T t){return(n.value()+q.value())*u/t;}
void display()const {q.show();n.show();}
};
然后我们放上main函数测试该类:
int main()
{
beta<double> g(3.5, 3);
g.display();
cout <<g.add(10, 2.3)<<endl;
return 0;
}
输出结果:
3.5
3
28
这里可能会稍微比较难理解一点,我们拆分开来看
template <typename T>
class beta
首先我们使用T作为beta的模板标识符,然后我们在它的private中定义了另一个类模板:
template <typename V>
class hold
{
private:
V val;
public:
hold(V v=0):val(v){}
void show()const{cout << val <<endl;}
V value()const {return val;}
};
我们使用V,然后在其private里面定义一个V val,那么在后面实例化的时候,val的具体类型由V来决定
在public中我们使用一个构造函数初始化val的值,show()展示val的值,最后一个value函数返回val的值(前面是val的类型是V,所以函数开头是V)
hold<T> q;
hold<int> n;
然后在beta 的private声明的最后,隐式的实例化了嵌入beta的类模板hold,使得beta的私有成员多了两个:一个是类型为T的q,一个是类型为int的n
public:
beta(T t, int i):q(t),n(i){}
template<typename U>
U add(U u,T t){return(n.value()+q.value())*u/t;}
void display()const {q.show();n.show();}
最后我们看到beta的public声明中的三个函数
第一个函数是构造函数,由于上面说了,在private中实例化了两个成员(还有一个是类模板,不需要实例化),所以这里使用初始化列表成员句法将其初始化;
第二个函数是一个函数模板,这里我们使用U来表示该函数模板,这里会传入两个参数:U类型的u, T类型的t,然后返回两个私有成员相加乘以u除以t的值,至于返回类型为什么是U,在main函数实例化的时候可以看到
第三个函数是display,用来展示私有成员的值,调用了类模板中的show方法
最后我们看看main函数都干了什么事情吧!
int main()
{
beta<double> g(3.5, 3);
g.display();
cout <<g.add(10, 2.3)<<endl;
return 0;
}
首先我们实例化类模板beta,类型定义为double,那么相应的私有成员q的类型就是double了;
然后我们调用display函数展示初始化的值
最后我们再输出add函数的值,这里我们输入了10 和 2.3,那么我们隐式的将U定义为int(10的类型),最后进行运算的时候会发生强制类型转换返回int值,就可以得到我们展示的结果啦!
模板作为参数
模板参数可以和常规参数一起混合使用:
template <template <typename T> class Thing, typename U, typename V>
class King
{
private:
Thing<U> a1;
Thing<V> a2;
……
}
友元模板
最后简单提一下友元(类与对象的内容太多了)
模板的友元分为3类:
- 非模板友元
- 约束模板友元:友元的类型取决于类被实例化时的类型
- 非约束模板友元:友元的所有具体化都是类的每一个具体化的友元
具体的内容不再展开,感兴趣的小伙伴可以私信或者在其他CSDN的博客中找到详细解释哟!
同样捞一捞整个系列的博文:
从小白开始学C++ 类与对象七(虚函数,protected成员,抽象基类)
从小白开始学C++类与对象五(返回类对象的方式,对象指针,嵌套结构,重载<<操作符)
从小白开始学C++ 类与对象三 (操作符重载、友元函数,类的自动转换和强制类型转换)
从小白开始学C++ 类与对象一(创建、定义类、构造函数、析构函数)
感谢您的耐心阅读!努力变成自己想要成为的人吧!