文章目录
public成员:外界可以访问
private成员:外界不可访问,子类中不可访问
protected成员:外界不可访问,子类(派生类)中可以访问,这样在派生类中可以访问某些外界不可访问的方法了
一、公有继承和虚函数
//"header.h
#ifndef HEADER_H_
#define HEADER_H_
#include<iostream>
#include<string>
#pragma warning(disable:4996)
using namespace std;
class T
{
private:
string name;
bool hasTable;
protected:
// void pp() { cout << "protested" << endl; }
public:
T(const string& n = "none", bool ht = false);
void Name() const { cout << name << endl; }
bool HasTable()const { return hasTable; }
void staicBinding() const { cout << "T\n"; }
virtual void show() const;
virtual ~T() {} //虚析构函数
};
class RT :public T//公有派生
{
private:
unsigned int rating;
public:
RT(unsigned int r = 0, const string& n = "none", bool ht = false);
RT(unsigned int, const T&);
int Rating()const { return rating; }
void staticBinding() const { cout << "RT\n"; }
//virtual void show() const;
void show() const; //派生类中加不加 virtual 都行,派生类的派生类也是如此。只需要在基类中添加virtual即可
//virtual ~RT() {} //同理,有没有都可以。
virtual void v() const {}
friend void Dss(RT& t) {
cout << t.rating << endl;
}
friend void sss(T& t) {
t.Name();
}
};
#endif
//"other.cpp"
#include "header.h"
#include<iostream>
T::T(const string& n,
bool ht)
{
name = n; // 调用string的构造函数,然后采用赋值运算符赋给 name
hasTable = ht;
}
void T::show() const {
cout << name << ", " << hasTable << endl;
}
//列表初始化调用基类的构造函数,如果程序不调用基类构造函数,程序默认使用默认的基类函数( 等同于 :TableTennisPlayer() )。
RT::RT(unsigned int r, const string& n, bool ht) :T(n, ht)
{
rating = r;
}
//调用基类的复制构造函数。在此采用默认的复制构造函数
RT::RT(unsigned int r, const T&
a) : T(a)
{
rating = r;
}
void RT::show() const {
cout << rating << ", ";
T::show();
}
#include "header.h"
#include<iostream>
void sss(T&);
int main()
{
using std::cout;
using std::endl;
T t1("bbb", false);
RT rt1(1140, "aaa", true);
// 基类指针或引用只能调用基类方法,不能调用派生类方法
rt1.Name();
rt1.Rating();
t1.Name();
RT rt2(1212, t1);
T* pt = &rt1;
// 静态联编 (static binding)
pt->staicBinding(); // T::staicBinding()
T t = rt2;
// 采用隐式重载赋值运算符:TableTennisPlayer& operator=(const TableTennisPlayer&) const;
// 基类引用指向一个 派生类对象, 将派生类的基类对象复制给基类。
// 虚函数: 动态联编 (dynamic binding)
t1.show();
rt2.show();
T* tt = &rt2;
tt->show(); // RT::show()
tt->staicBinding(); // T::staicBinding()
//virtual: 采用了virtual的方法,被指针/引用 调用时,根据其具体指向的类型来调用对应的方法
//否则,根据指针/引用 的类型来调用方法
T* rt = new RT(2, "haha", true);
delete rt; // 因为前面采用了虚析构函数,所以按照动态联编的规则,
//动态调用 RT 的(默认)析构函数。
//虚函数表:
//每个对象内部含有一个隐藏成员,其保存了一个指向数组的指针,这个数组里保存的是当前对象的虚函数地址。
//该数组称为虚函数表。
//如:T对象的虚函数表中只有一项(因为 T 中只有一个虚函数): 一个指向 T::show() 的地址,
//假设该地址为 0x1;
//而RT对象的虚函数表有两项:
//一个是 RT::show() 的地址,这个地址和T::show() 肯定不一样,假设该值为 0x2;
//另一个是指向虚函数 RT::v() 的地址值,该地址值假设为0x3。
//在执行 T* tt = &rt2; tt->show(); 时,程序所查找的是RT::show()的地址 0x2,然后执行。
//虚函数效率较低,因为其多了一项查表的行为。
Dss(rt1);
sss(t1);//不可访问,因为 sss 是 RT的友元函数,而它的参数又不是 RT 类型,所以无法找到 sss 函数的定义。 添加全局声明即可
return 0;
}
公有继承/派生:
存储了基类的数据成员(派生类继承了基类的实现);
派生类对象可以使用基类的方法(派生类继承了基类的接口)
在派生类(类方法实现)中可以访问基类的公有方法和保护方法。
创建派生类对象时,程序首先调用基类的构造函数,然后调用派生类的构造函数。析构时刚好相反。
虚函数:
友元函数不能是虚函数,只有类成员才能是虚函数
没有重新定义虚函数,会使用离它最近的基类的虚函数版本
如果重新定义一个同名方法,但是参数列表不同,就会隐藏了同名的基类的虚函数。 但是返回值可以由返回基类的指针/引用变成返回派生类的指针/引用(返回类型协变)。如果基类中的虚函数发生重载(存在多个同名的虚函数),那么在派生类中需要定义所有重载的虚函数(不然基类中的重载的一些函数会被隐藏,派生类汇中无法进行访问)。
二、抽象基类
#include<iostream>
#include<string>
using std::string;
using std::cout;
using std::endl;
class A
{
private:
string name;
long id;
protected:
const string& getName() const { return name; }
long getId() const { return id; }
public:
A(const string& n = "haha", long i = -1) :name(n), id(i) {}
//virtual void show() = 0; // 虚函数使用 =0 表示其为一个纯虚函数
//含有至少一个纯虚函数,则该类为抽象基类,无法创建其对象。
virtual void show() = 0 { // 抽象基类也可以有函数定义
cout << name << ", " << id << endl;
}
virtual ~A() {};
};
class B :public A
{
private:
double height;
public:
B(const string& s = "haha", long i = -1, double h = 180.1) :A(s, i), height(h) {}
virtual void show() {
A::show();
cout << height << ", " << getName() << ", " << getId() << endl;
}
virtual ~B() {};
};
class C :public A
{
private:
double weight;
public:
C(const string& s = "Nullbody", long i = -1, double w = 100.2) :A(s, i), weight(w) {}
virtual void show() {
cout << weight << ", " << A::getName() << ", " << A::getId() << endl;
}
void setW(double a) { weight = a; }
};
int main()
{
using namespace std;
//A a;
B b;
C c;
b.show();
c.show();
return 0;
}
三、动态内存分配
#include<iostream>
using namespace std;
class BD
{
private:
char* label;
int rating;
public:
BD(const char* l = "null", int r = 0);
BD(const BD&);
virtual ~BD();
BD& operator=(const BD&);
friend std::ostream& operator<<(std::ostream&, const BD&);
};
class D :public BD // 如果派生类中存在动态内存分配,那么需要显式定义 构造函数,析构函数,复制构造函数和赋值运算符
{
private:
char* style;
public:
D(const char* l = "null", int r = 0, const char* s = "none");
D(const char* s, const BD&);
D(const D&);
~D();
D& operator=(const D&);
friend std::ostream& operator<<(std::ostream&, const D&);
};
BD::BD(const char* l, int r)
{
int temp = strlen(l);
label = new char[temp + 1];
strcpy_s(label, temp + 1, l);
rating = r;
}
BD::BD(const BD& a)
{
rating = a.rating;
int temp = strlen(a.label);
label = new char[temp + 1];
strcpy_s(label, temp + 1, a.label);
}
BD::~BD()
{
delete[]label;
}
BD& BD::operator=(const BD& a)
{
if (&a == this)
return *this;
rating = a.rating;
int temp = strlen(a.label);
delete[] label;
label = new char[temp + 1];
strcpy_s(label, temp + 1, a.label);
return *this;
}
ostream& operator<<(ostream& os, const BD& a)
{
os << "Label: " << a.label << ", " << a.rating << endl;
return os;
}
D::D(const char* l /* = "null" */, int r /* = 0 */,
const char* s /* = "none" */) :BD(l, r)
{
int temp = strlen(s);
style = new char[temp + 1];
strcpy_s(style, temp + 1, s);
}
D::D(const char* s, const BD& a) :BD(a)
{
int temp = strlen(s);
style = new char[temp + 1];
strcpy_s(style, temp + 1, s);
}
D::D(const D& s) :BD(s)
{
int temp = strlen(s.style);
style = new char[temp + 1];
strcpy_s(style, temp + 1, s.style);
}
D::~D()
{
delete[] style;
}
D& D::operator=(const D& a)
{
if (&a == this)
return *this;
BD::operator=(a);
delete[] style;
style = new char[strlen(a.style) + 1];
strcpy_s(style, strlen(a.style) + 1, a.style);
return *this;
}
ostream& operator<<(ostream& os, const D& a)
{
os << (const BD&)a;
os << "Style: " << a.style << endl;
return os;
}
int main()
{
BD shirt("BD", 8);
D map("BD", 5, "D");
cout << "Displaying D object: \n";
cout << shirt << endl;
cout << "Displaying D object: \n";
cout << map << endl;
D map2;
map2 = map;
cout << map2 << endl;
return 0;
}
四、包含和私有继承
is-a :公有继承,派生类中可以访问基类的公有/保护成员,它们是派生类的公有成员。基类的接口是派生类的公有接口,外界可以访问。
has-a:
包含,派生类中可以访问基类的公有/保护成员,外界不能通过派生类访问基类的公有成员。
私有继承,派生类中可以访问基类的公有/保护成员,它们是派生类的私有成员。基类的接口是派生类私有接口,外界不可以访问。
包含中,相当于有一个有名字的基类对象,而在私有继承中,相当于有一个没有命名的基类对象。
包含
// header.h
#ifndef HEADER_H_
#define HEADER_H_
#include<iostream>
#include<vector>
using namespace std;
#include<string>
class Student
{
private:
vector<double> scores;
string name;
ostream& arr_out(ostream&) const;
public:
Student() :name("Null student"), scores() {}
explicit Student(const string& a) :name(a), scores() {} //为了避免隐式构造函数将 string 作为参数创建出一个 Student 对象
explicit Student(int& a) :name("Nully"), scores(a) {} //同上
Student(const string& a, int b) :name(a), scores(b) {}
Student(const string& a, const vector<double>& b) :name(a), scores(b) {}
Student(const char* a, double* b, int n) :name(a) {
for (int i = 0; i < n; i++)
scores.push_back(b[i]);
}
~Student() {};
double Average() const;
const string& Name() const;
double& operator[](int i);
double operator[](int i) const;
friend istream& operator>>(istream&, Student&);
friend istream& getline(istream&, Student&);
friend ostream& operator<<(ostream&, const Student&);
};
#endif
//other.cpp
#include "header.h"
double Student::Average()const {
if (scores.size() == 0)
return 0;
double sum = 0;
for (double d : scores)
sum += d;
return sum / scores.size();
}
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 lim = scores.size();
if (lim){
for (i = 0; i < lim; i++)
os << scores[i] << ' ';
cout << endl;
}
else
os << " empty array!\n";
return os;
}
istream& operator>>(istream& is, Student& a){
is >> a.name;
return is;
}
istream& getline(istream& is, Student& a){
getline(is, a.name);
return is;
}
ostream& operator<<(ostream& os, const Student& a){
os << a.Name() << endl;
a.arr_out(os);
return os;
}
私有继承
//header.h
#ifndef HEADER_H_
#define HEADER_H_
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Student :private string, private vector<double>
{
private:
typedef vector<double> v;
ostream& arr_out(ostream&) const;
public:
using string::size;
Student() :string("Null student"), v() {}
explicit Student(const string& a) :string(a), v() {}
explicit Student(int a) :string("Nully"), v(a) {}
Student(const string& a, int b) :string(a), v(b) {}
Student(const string& a, const v& b) :string(a), v(b) {}
Student(const char* a, double* b, int n) :string(a) {
for (int i = 0; i < n; i++)
v::push_back(b[i]);
}
~Student() {};
double Average() const;
const string& Name() const;
double& operator[](int i);
double operator[](int i) const;
friend istream& operator>>(istream&, Student&);
friend istream& getline(istream&, Student&);
friend ostream& operator<<(ostream&, const Student&);
};
#endif
//other.cpp
#include "header.h"
double Student::Average()const {
if (v::size() == 0)
return 0;
double sum = 0;
for (double d : (v)*this)
sum += d;
return sum / v::size();
}
const string& Student::Name() const {
return (const string&)*this;
}
double& Student::operator[](int i)
{
return v::operator[](i);
}
double Student::operator[](int i) const {
return v::operator[](i);
}
ostream& Student::arr_out(ostream& os) const {
int i;
int lim = v::size();
if (lim){
for (i = 0; i < lim; i++)
os << v::operator[](i) << ' ';
cout << endl;
}
else
os << " empty array!\n";
return os;
}
istream& operator>>(istream& is, Student& a){
is >> (string&)a;
return is;
}
istream& getline(istream& is, Student& a){
getline(is, (string&)a);
return is;
}
ostream& operator<<(ostream& os, const Student& a){
os << (const string&)a<< endl;// 调用基类的友元函数
a.arr_out(os);
return os;
}
1,采用作用域解析符在类中访问基类的公有、保护成员
2,通过强制类型转换,访问基类对象
3,访问基类的友元函数时,在派生类的友元函数中,将参数从派生类强制类型转换为基类类型。
一般情况下,应该使用包含。在需要访问基类的保护成员时,或者需要重新定义虚函数时,应该使用私有继承。
保护继承:派生类中可以访问基类的公有/保护成员,它们是派生类的保护成员。基类的接口是派生类保护接口,外界不可以访问。它在私有继承的区别在于第三代类中可以使用基类的公有/保护成员
如果想在外界访问私有/保护继承中的基类方法:1,在派生类中重新定义一个公有方法(其中调用了想访问基类方法)2,在派生类的公有成员中,采用using声明,仅仅只需要使用成员名即可。如:using string::size;
五、多重公有继承
#include<iostream>
#include<string>
using namespace std;
class A //抽象基类
{
private:
string name;
public:
A() :name("no one") {}
A(const string& s) :name(s) {}
virtual ~A() = 0 {} //纯虚函数
virtual void show() const;
};
class B : public A
{
private:
int id;
public:
B() :A(), id(0) {}
B(const string& s, int b = 0) :A(s), id(b) {}
B(const A& a, int b = 0) :A(a), id(0) {}
void show() const;
};
class C : public A
{
private:
double income;
public:
C() :A(), income(0) {}
C(const string& s, double b = 0) :A(s), income(b) {}
C(const A& a, double b = 0) :A(a), income(b) {}
void show() const;
};
//A::~A() {}
void A::show() const {
cout << "Name: " << name << '\n';
}
void B::show() const {
cout << "ID " << id << ", ";
A::show();
}
void C::show() const {
cout << "Income: " << income << ", ";
A::show();
}
int main()
{
B b("b", 5);
C c("c", 3.14);
B bb;
C cc;
A* pw[] = { &b, &c, &bb,&cc };
for (int i = 0; i < 4; i++)
pw[i]->show();
return 0;
}
假设一个类 D 继承 B 和 C,如:class D:public B,public C{…};
那么一个D对象中将包含两个A对象,B中一个,C中一个。那么在使用A指针指向D时,不能直接写成:A* p=&D; 而是应该写成 A* p=(B*)&D; 或者A* p=(C*)&D;
为了避免在D对象中包含两个A对象,采用虚基类可以使得派生类只继承一个基类(A)对象。如下:
虚基类
#include<iostream>
#include<string>
using namespace std;
class A //抽象基类
{
private:
string name;
public:
A() :name("no one") {}
A(const string& s) :name(s) {}
virtual ~A() = 0 {} //纯虚函数
virtual void show() const;
};
class B : virtual public A
{
private:
int id;
public:
B() :A(), id(0) {}
B(const string& s, int b = 0) :A(s), id(b) {}
B(const A& a, int b = 0) :A(a), id(0) {}
void show() const;
int get()const { return id; }
};
class C : public virtual A
{
private:
double income;
public:
C() :A(), income(0) {}
C(const string& s, double b = 0) :A(s), income(b) {}
C(const A& a, double b = 0) :A(a), income(b) {}
void show() const;
double get()const { return income; }
};
class D : public B, public C
{
public:
D() {};
// 构造函数中必须先初始化抽象基类 A,然后在初始化虚基类 B 和 C
D(const string& s, int p = 0, double v = 0)
:A(s), B(s,p), C(s,v) {}
D(const A& a, int p = 0, int v = 0)
:A(a), B(a, p), C(a, v) {}
D(const B& b, int v = 0)
:A(b), B(b), C(b, v) {}
D(const C& c, int p = 0)
:A(c), B(c, p), C(c) {}
void show() const;
};
void A::show() const {
cout << "Name: " << name << '\n';
}
void B::show() const {
cout << "ID " << id << ", ";
A::show();
}
void C::show() const {
cout << "Income: " << income << ", ";
A::show();
}
void D::show() const {
cout << "ID " << B::get() << ", ";
cout << "Income: " << C::get() << ", ";
A::show();
}
int main()
{
B b("b", 5);
C c("c", 3.14);
B bb;
C cc;
A* pw[] = { &b, &c, &bb,&cc };
for (int i = 0; i < 4; i++)
pw[i]->show();
D d;
d.show();
return 0;
}
六、模板类
模板类中关于成员函数模板定义有三种方法,一种是在类中定义,另一种是在类外同一个文件中定义,最后一种是在类外不同文件中定义。
第一种,类中定义:
header.hpp:
#pragma once
#include<iostream>
using namespace std;
template <class Type>// 模板类
class Gen
{
private:
int top;
public:
void show(){
Type tmp = "abcd";
cout<< tmp<<endl;
}
};
main.cpp:
#include "header.hpp"
using namespace std;
int main(){
Gen<string> gen;
gen.show();
return 0;
}
第二种,类外同一个文件定义:
header.hpp:
#include<iostream>
using namespace std;
template <typename Type>// 模板类
class stack
{
private:
enum { max = 10 };
Type arr[max];
int top;
public:
stack(int a=max) :top(0),max(a) {}//默认构造函数
void push(const Type& a);
void pop(Type& a);//
};
template<typename Type> // 类定义中的函数都是模板函数
void stack<Type>::push(const Type& a){
arr[top++] = a;
}
template<typename Type>
void stack<Type>::pop(Type& a){
a = arr[--top];
}
main.cpp:
#include "header.hpp"
using namespace std;
int main()
{
stack<string> st; //数组大小为默认的 10;
st.push("hhhh");
string a("oo");
st.pop(a);
cout << a << endl;
stack<string>a(5);//修改数组大小为 5
// using 等价于 typedef
using ss = stack<string>;
using si = stack<int>;
using I = int;
return 0;
}
第三种,类外不同文件中定义:
header.hpp:
#pragma once
#include<iostream>
template <class Type>// 模板类
class Gen
{
private:
int top;
public:
void show();
};
header.cpp中的模板函数需要采用如下写法。
#pragma once
#include "header.hpp"
using namespace std;
template <class Type>
void Gen<Type>::show(){
Type tmp = "abcd";
cout<< tmp<<endl;
}
main.cpp 中的需要添加语句 #include "header.cpp"
否则在链接时会报错(找不到 show() 所属类的引用):
#include "header.hpp"
#include "header.cpp"
int main(){
Gen<std::string> gen;
gen.show();
return 0;
}
七、友元类
#include<iostream>
using namespace std;
class Tv
{
private:
int state;
enum { Off, On };
void onoff();
public:
friend class Remote; //友元类声明的所在位置在 哪都可以(公有,私有,保护)
Tv(int s = Off ) :state(s){}
bool ison() const{ return state == On; }
};
void Tv::onoff() {
state = (state == On) ? Off : On;
}
class Remote
{
public:
Remote(){}
void onoff(Tv& t) { t.onoff(); }
};
int main(){
Tv s42;
cout << s42.ison() << endl;
Remote grey;
grey.onoff(s42);
cout << s42.ison() << endl;
return 0;
}