文章目录
【C++实战二】继承和多态初体验
1. 继承访问权限测试
继承的作用:当我们创建一个类时,不一定需要重新定义新的成员函数或变量,可以指定新建的类继承已有类的成员。这种模式使得创建和维护程序变得容易,且提高编码效率。(这里的已有类称为基类,继承类称为派生类)
C++的三种继承方式:public,protected,private
接下来我们就通过代码验证不同继承下的访问权限。
1.1 public继承
#include<iostream>
using namespace std;
// 创建一个基类
class One
{
// 基类的私有变量
private:
void test1()
{
cout << "My name is One's Praivate." << endl;
}
// 基类的保护变量
protected:
void test2()
{
cout << "My name is One's Protected." << endl;
}
// 基类的公共变量
public:
void test3()
{
cout << "My name is One's Public." << endl;
}
};
// 基类的派生类(公共继承)
class Two:public One
{
public:
void two_func()
{
test2();
test3();
}
};
// 基类的派生类的派生类(公共继承)
class Three:public Two
{
public:
void three_func()
{
test2();
test3();
}
};
int main()
{
One one;
one.test3();
cout << "------------" << endl;
Two two;
//two.test1(); // ERROR: public继承下,派生类对象不可以访问基类的private成员
//two.test2(); // ERROR: public继承下,派生类对象不可以访问基类的protected成员
two.test3(); // public继承下,派生类对象可以访问基类的public成员
two.two_func();
cout << "------------" << endl;
Three three;
//three.test1(); // ERROR: public继承下,派生类对象不可以访问基类的private成员
//three.test2(); // ERROR: public继承下,派生类对象不可以访问基类的protected成员
three.test3(); // public继承下,派生类对象可以访问基类的public成员
three.three_func();
return 0;
}
运行结果: My name is One's Public. ------------ My name is One's Public. My name is One's Protected. My name is One's Public. ------------ My name is One's Public. My name is One's Protected. My name is One's Public. 结果分析: 1. public继承下,派生类以及基类派生类的派生类的成员可以访问基类具有public和protected权限的成员。 2. public继承下,派生类对象可以访问基类的public成员。 总结:基类的public和protected成员被继承后,保持原来的状态
1.2 protected继承
#include<iostream>
using namespace std;
// 创建一个基类
class One
{
// 基类的私有变量
private:
void test1()
{
cout << "My name is One's Praivate." << endl;
}
// 基类的保护变量
protected:
void test2()
{
cout << "My name is One's Protected." << endl;
}
// 基类的公共变量
public:
void test3()
{
cout << "My name is One's Public." << endl;
}
};
// 基类的派生类(保护继承)
class Two:protected One
{
public:
void two_func()
{
test2();
test3();
}
};
// 基类的派生类的派生类(保护继承)
class Three:protected Two
{
public:
void three_func()
{
test2();
test3();
}
};
int main()
{
Two two;
//two.test1(); // ERROR: protected继承下,派生类对象不可以访问基类的private成员
//two.test2(); // ERROR: protected继承下,派生类对象不可以访问基类的protected成员
//two.test3(); // ERROR: protected继承下,派生类对象不可以访问基类的public成员
two.two_func();
cout << "------------" << endl;
Three three;
//three.test1(); // ERROR: protected继承下,派生类对象不可以访问基类的private成员
//three.test2(); // ERROR: protected继承下,派生类对象不可以访问基类的protected成员
//three.test3(); // ERROR: protected继承下,派生类对象不可以访问基类的public成员
three.three_func();
return 0;
}
运行结果: My name is One's Protected. My name is One's Public. ------------ My name is One's Protected. My name is One's Public. 结果分析:在protected继承下,派生类的成员可以访问基类的public和protected成员。但是派生类对象访问基类的变量均会报错。 总结:protected继承将基类的public和protected成员权限转换为protected。
为了探索protected在一次继承后是否对基类的成员权限产生本质上的变化,将经过protected的派生类再通过public继承,观察其结果。
// 基类的派生类(保护继承)
class Two:protected One
{
public:
void two_func()
{
test2();
test3();
}
};
// 经过保护继承后的派生类再经过公共继承
class Three:public Two
{
public:
void three_func()
{
test2();
test3();
}
};
int main()
{
Three three;
three.test3();
return 0;
}
运行结果: error: 'void One::test3()' is inaccessible 总结:当出现一次protected继承后,派生类对象便不可以访问基类的任何成员,并且一直按照这个性质向下继承。
1.3 private继承
#include<iostream>
using namespace std;
// 创建一个基类
class One
{
// 基类的私有变量
private:
void test1()
{
cout << "My name is One's Praivate." << endl;
}
// 基类的保护变量
protected:
void test2()
{
cout << "My name is One's Protected." << endl;
}
// 基类的公共变量
public:
void test3()
{
cout << "My name is One's Public." << endl;
}
};
// 基类的派生类(私有继承)
class Two:private One
{
public:
void two_func()
{
test2();
test3();
}
};
// 基类的派生类的派生类(私有继承)
class Three:private Two
{
public:
void three_func()
{
// test2();
// test3();
}
};
int main()
{
Two two;
// two.test1(); // ERROR: private继承下,派生类对象不可以访问基类的private成员
// two.test2(); // ERROR: private继承下,派生类对象不可以访问基类的protected成员
// two.test3(); // ERROR: private继承下,派生类对象不可以访问基类的public成员
two.two_func();
cout << "------------" << endl;
//Three three;
//three.three_func(); //ERROR: private继承下,进一步继承的成员也无法访问基类的任何成员
return 0;
}
运行结果: My name is One's Protected. My name is One's Public. ------------ 结果分析: 1. private继承的派生类对象无法访问基类成员,但是派生类成员可以访问基类中的public权限和protected权限的成员。 2. private的进一步继承的派生类(基类的孙子)不管是对象还是成员都无法访问基类成员。 总结:private继承将基类的public和protected成员权限转换为private。 基类private属性的成员永远无法访问。
1.4 使用using提升权限
当我们对基类protected成员有需求时,常适用using将将基类成员的protected提升为public,追求程序的多样性。
class Two:public One
{
public:
// 使用using将基类中的protected权限提升至public权限
using One::test2;
void two_func()
{
test2();
test3();
}
};
class Three:public Two
{
public:
void three_func()
{
test2();
test3();
}
};
int main()
{
Two two;
// two.test1(); //using函数不适用private成员
two.test2();
Three three;
// three.test1(); //using函数不适用private成员
three.test2();
return 0;
}
2. 友元类继承测试
类A将类B声明为自己的友元类,则类B 的所有成员可以访问类A 的私有对象
友元类声明:friend class 类名
#include<iostream>
using namespace std;
// 设计类A含有私有变量a,在类A中友元给类C
class A
{
private:
void test_a()
{
cout << "A: You can look me!" << endl;
}
friend class C;
};
// 设计类B继承A,添加私有变量b;在类C中测试访问类B的成员变量a, b
class B:public A
{
private:
void test_b()
{
cout << "B: You can look me!" << endl;
}
};
class C
{
public:
void test_c()
{
B b1;
b1.test_a(); // 因为C是A的友元类,因此C可以访问A的private成员
//b1.test_b(); // C无法访问B的private成员
}
};
int main()
{
C c;
c.test_c();
return 0;
}
运行结果: A: You can look me! 结果分析:友元类可以访问声明类的私有变量,否则私有变量不被访问。
设计类D继承C,在D的成员函数中测试访问类A的成员变量a,类B的成员变量a, b。代码如下:
class D:public C
{
public:
void test_d()
{
A a1;
//a1.test_a();
B b1;
//b1.test_a();
//b1.test_b();
}
};
运行结果:D继承C后,D的成员函数无法访问类A或类B的私有变量。 结果分析:父亲的朋友不是儿子的朋友
3. C++的多态性
多态性可以理解为不同对象收到相同的消息,产生不同的行为。在C++中,多态性可以通过同一个名字定义不同的函数,这些函数执行不同的行为,实现“一个接口,多种方法”。
3.1 一般多态性
#include<iostream>
using namespace std;
// 定义基类作为父类
class Father
{
public:
void test()
{
cout << "I am Father" << endl;
}
};
// 定义Son1公共继承基类
class Son1:public Father
{
public:
void test()
{
cout << "I am Son1" << endl;
}
};
// 定义Son2公共继承基类
class Son2:public Father
{
public:
void test()
{
cout << "I am Son2" << endl;
}
};
void num(int a)
{
cout << "Exiet one number" << endl;
}
void num(int a,int b)
{
cout << "Exiet two number" << endl;
}
int main()
{
// 测试不同对象调用同一函数的多样性
Father f;
Son1 s1;
Son2 s2;
f.test();
s1.test();
s2.test();
cout << "---------" << endl;
// 测试函数重载
int a=1;
int b=2;
num(a);
num(a,b);
}
运行结果: I am Father I am Son1 I am Son2 --------- Exiet one number Exiet two number 结果分析:不同对象调用同一函数可以实现不同操作,函数重载也可以。所以说这两种方式都体现了多态性。
3.2 虚函数
当依照上述的类,我们实现以下main函数。
int main()
{
Father *f;
f = new Son1;
f -> test();
}
运行结果: I am Father 结果分析:虽然f申请了新的Son1对象,但是调用函数时,执行的是父亲的test()函数 解决方法:将基类的函数改为虚函数,既在函数定义前加上virtual 总结:虚函数是实现c++多态的一个重要途径:在基类中将被重写的成员函数设置为虚函数,其含义是:当通过基类的指针或者引用调用该成员函数时,将根据指针指向的对象类型确定调用的函数,而非指针的类型。
// 定义基类作为父类
class Father
{
public:
virtual void test()
{
cout << "I am Father" << endl;
}
};
3.3 特殊多态性
在程序构建过程中,实现函数传参过程的多样性是至关重要的。
// 定义一个全局函数,希望该函数能保留派生类的多态方法
void identify(Father a)
{
a.test();
}
int main()
{
Father f;
Son1 s1;
Son2 s2;
identify(f);
identify(s1);
identify(s2);
return 0;
}
运行结果: I am Father I am Father I am Father 结果分析:该结果并没有体现传参的多样性,并不是我们预想的
解决方法一:将传入的参数改为基类的指针
void identify(Father *a)
{
a->test();
}
int main()
{
Father f;
Son1 s1;
Son2 s2;
identify(&f);
identify(&s1);
identify(&s2);
return 0;
}
解决方法二:将传入的参数改为基类的引用
void identify(Father &a)
{
a.test();
}
int main()
{
Father f;
Son1 s1;
Son2 s2;
identify(f);
identify(s1);
identify(s2);
return 0;
}
3.4 析构函数的多态性
// 定义基类作为父类
class Father
{
public:
virtual void test()
{
cout << "I am Father" << endl;
}
// 析构虚函数
virtual ~Father()
{
cout << "destory1" << endl;
}
};
// 定义Son1公共继承基类
class Son1:public Father
{
public:
void test()
{
cout << "I am Son1" << endl;
}
// 析构函数
~Son1()
{
cout << "destory2" << endl;
}
};
int main()
{
Father *f = new Son1;
f->test();
delete f;
return 0;
}
运行结果: I am Son1 destory2 destory1 结果分析:基类的析构函数添加virtual属性后,delete子类时先执行子类的析构函数再执行父类的析构函数。
3.5 虚继承和多继承
当A作为基类,B和C是A的子类,D是B和C的子类,这样会导致D继承了2次A。这种多继承的情况下应该采取虚继承。
#include<iostream>
using namespace std;
class A{
public:
int a = 100;
};
class B:public A{
};
class C:public A{
};
class D:public B,public C{
public:
void test(){
// ERROR: 无法判断A来自B还是C
int i = a;
cout << i << endl;
}
};
int main()
{
D d;
d.test();
return 0;
}
//解决方法(虚继承):
class B:virtual public A{
};
class C:virtual public A{
};
4. 总结
通过本次实验对继承访问权限,友元类以及多态性有了基础性的摸底。
继承访问权限中通过对于不同对象的权限设置以及不同方式的继承有利于丰富程序的多样性。
友元类是我们对于类中的私有成员的开小灶权力,体现了C++语言的灵活性,方便我们对于一些特定要求进行代码编。
多态性是对于编码者及其友好的性质,通过一个接口,多种调用的方式,使得程序功能变得完善且极大程度上降低了编码时间成本。