面向对象程序设计
面向对象程序设计(OOP)的核心思想是数据抽象、 继承和动态绑定。
- 使用数据抽象, 我们可以将类的接口与实现分离(封装)。
- 使用继承, 我们可以定义相似的类型并对其相似关系建模。
- 使用动态绑定, 可以在一定程度上忽略相似类型的区别, 而以统一的方式使用它们的对象(多态)
类之间的关系
类之间的三种关系:
- 包含关系(has-A)
class B{ private: A a;}
- 使用关系(uses-A)
class B{public: void method(A &a);}
- 继承关系(is-A)
class B: public A{}
继承的概念
继承: 通过继承联系在一起的类构成一种层次关系。
- 通常在层次关系的根部有一个基类, 也称为父类。
- 其他类直接或间接地从基类继承而来, 这些继承得到的类称为派生类, 也称为子类。
- 基类负责定义在层次关系中所有类共同拥有的成员, 而每个派生类定义各自特有的成员。
继承关系示例
- 继承具有传递性, U3D程序员、 UE程序员、 Java程序员都属于程序员。
- 继承不具有对称性, 不是所有的程序员都是U3D程序员。
- 基类是对派生类的抽象, 派生类是对基类的具体化。
派生类的定义
定义派生类的一般形式:
class 派生类名:类派生列表
{
成员列表
}
注意:
- 除了增加基类列表外, 派生类的定义与类的定义并无区别。
- 派生类的成员列表描述的是派生类自己新增加的数据成员和成员函数。
- 类派生列表指定了一个或多个基类, 由访问说明符和基类的名称构成。
class Derived : public Base1, public Base2
{
成员列表
}
- 基类名必须是已定义的类的名字。
- 访问说明符表示派生类对基类的继承方式: public(公有继承)/private(私有继承)/protected(保护继承)。
派生类的构成
- 派生类拥有基类所有的数据成员和成员函数。
- 派生类可以拥有基类没有的数据成员和成员函数。
- 可以通过访问说明符控制成员的访问权限。
- 可以通过在派生类中声明同名的成员, 来实现修改基类成员功能的效果。
- 派生类是一种特殊的基类, 可以将派生类对象当作父类对象使用。
- 友元函数不能被继承, 友元函数不是成员函数。
#include<iostream>
using namespace std;
//基类, 拥有2个数据成员和一个成员方法
class Base
{
public:
int bnum1_;
int bnum2_;
void print()
{
cout << "Base" << endl;
cout <<" bnum1_ :" << bnum1_ << " bnum2_ :" << bnum2_ << endl;
}
};
//派生类,继承自Base
class Derived : public Base
{
public:
int dnum_; //派生类中新增加的数据成员
void print() //同名函数,可以修改基类中同名的函数的功能
{
cout << "Derived" << endl;
cout << " bnum1_ :" << bnum1_ << " bnum2_ :" << bnum2_ << "dnum_ :" << dnum_ << endl;
}
};
int main()
{
Base b1;
b1.bnum1_ = 10;
b1.bnum2_ = 20;
Derived d1;
d1.bnum1_ = 10;
d1.bnum2_ = 30;
d1.dnum_ = 5;
b1.print();
d1.print();
d1.Base::print(); //如果想要调用基类中的方法,可以使用作用域操作符::
return 0;
}
派生类的访问控制
单个类的访问控制:
- public(公有成员): 在类的内部和外部都可以访问的成员。
- private(私有成员): 在类的内部可以访问, 在类的外部不可以访问, 只能通过成员函数或友元函数进行访问。
protected(保护成员) : 与私有成员类似, 但在派生类中可以访问。
在派生类内部(成员函数或友元函数)使用基类成员时: 不受继承方式的影响, 只看该成员在基类中的访问属性。
在派生类外部(派生类用户)使用基类成员时: 不同的继承方式决定了基类成员在派生类中的访问属性, 从而对派生类用户的访问权限产生影响。
public继承, 所有基类成员在派生类中保持原有的访问级别。
- protected继承, public–protected, protected-protected,private-private。
private继承, 所有基类成员在派生类中变为private成员。
#include<iostream>
using namespace std;
class Base
{
public:
int bnum1_;
private:
int bnum2_;
protected:
int bnum3_;
public:
//在类的内部,都可以访问到,通过成员方法,或者是友元函数
void setNum(int bnum1, int bnum2, int bnum3)
{
bnum1_ = bnum1;
bnum2_ = bnum2;
bnum3_ = bnum3;
}
friend void print(Base &b)
{
cout << b.bnum1_ << " " << b.bnum2_ << " " << b.bnum3_ << endl;
}
};
class Derived : public Base
{
public:
int dnum1_;
private:
int dnum2_;
protected:
int dnum3_;
public:
//在派生类内部只能访问到基类的public成员和protected成员,不能访问基类的private成员。
//不看派生类的继承方式
void setNum(int dnum1, int dnum2, int dnum3)
{
dnum1_ = dnum1;
dnum2_ = dnum2;
dnum3_ = dnum3;
}
friend void printD(Derived &d)
{
d.bnum1_ = 10;
//d.bnum2_ = 17;//基类的私有成员,在派生类中不可以访问
d.bnum3_ = 15;
cout << d.dnum1_ << " " << d.dnum2_ << " " << d.dnum3_ << endl;
}
};
class Derived1 : private Base
{
public:
void print()
{
//bnum2_ = 10; //基类的私有成员,在派生类中不可以访问
cout << bnum1_ << " " << bnum3_ << endl; //基类的公有成员和保护成员,在派生类中可以访问
}
};
class Derived2 : protected Base
{
public:
void print()
{
//bnum2_ = 10; //基类的私有成员,在派生类中不可以访问
cout << bnum1_ << " " << bnum3_ << endl; //基类的公有成员和保护成员,在派生类中可以访问
}
};
int main()
{
Base b;
b.bnum1_ = 10;
//b.bnum2_ = 10; //在类的外部不可以访问,private
//b.bnum3_ = 15; //在类的外部不可以访问,protected
b.setNum(14, 16, 17);
print(b);
//在派生类的外部访问基类中的成员时,会根据继承方式影响基类成员的访问级别
//1.public 继承
Derived d;
d.bnum1_ = 10; //public - public, 可以被访问
//d.bnum2_ = 15; //private -- private ,不可以被访问
//d.bnum3_ = 10; //protected -- protected, 不可以被访问
//2.private 继承
Derived1 d1;
//d1.bnum1_ = 15; //public -- private,不可以被访问
//d1.bnum2_ = 10; //private -- private, 不可以被访问
//d1.bnum3_ = 20; //protected -- private, 不可以被访问
//3.protected 继承
Derived2 d2;
//d2.bnum1_ = 10; //public -- protected,不可以被访问
//d2.bnum2_ = 16; //private -- private, 不可以被访问
//d2.bnum3_ = 9; //protected -- protected,不可以被访问
return 0;
}
访问属性设置的原则:
- 需要被外界访问的成员, 设置为: public。
- 只能在当前类中访问的成员, 设置为: private。
- 只能在当前类和派生类中访问的成员, 设置为: protected。