Chapter 25.面向对象编程

面向对象思想

抽象 继承 封装 多态(动态绑定) java/c++

动态绑定

通过基类引用或指针调用虚函数时,发生动态绑定
这里说明了动态绑定的两个条件
1.调用函数为虚函数
2.只有基类引用或指针来调用
时,从这点上讲C++还不能完全支持面向对象编程,还要用指针和引用来实现
引用或指针可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键
由被调用的引用或指针的实际类型决定动态绑定的成员函数

基类一般都要定义虚析构函数

非static成员函数和构造函数外,都可为虚函数

派生类

在派生类中只能通过派生类对象来访问基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限

eg:
void Derived::memFcn(Derived &d,Base &b)//派生类中对base类的protected没有访问权
{
    d.baseDataMem;//ok
    b.baseDataMem;//error
}
                    用户
private        类本身成员
protected   从该类派生新类的程序员,提供给其的接口就是protected成员和public成员的组合
public         该类的用户

一旦在基类中定义为virtual函数,那么一直为virtual函数,派生类不能改变该函数为virtual函数

派生类中virtual函数的声明必须与基类中的声明相同,有一个例外,如果基类中virtual函数返回基类的指针/引用,则派生类中virtual函数可以返回基类/派生类的指针/引用
派生类包括基类public成员和protected成员
派生类中有时候需要覆盖虚函数,比如需要调用基类对应的虚函数又要调用派生类的虚函数时,应该这样做
eg:
//派生类虚函数中
Derived::virtualFcn()
{
    Base::virtualFcn();//先调用基类的虚函数
    //再实现派生类中特有操作
}
如果虚函数有默认实参,那么基类虚函数和派生类虚函数的默认实参应该保持一致
派生类可以进一步限制但不能放松对所继承的成员的访问
如果基类成员为public或protected,派生列表中使用的访问标号决定该成员在派生类中的访问级别
public继承,基类的public或protected直接继承过来
protected继承,基类的public或protected为protected    //protected & private在实现中使用被继承类,但基类中的接口未成为其接口,这样的派生称为实现继承
private继承,基类的public或protected都为private

is a      //派生类is a基类 -- 是一种

has a   //类中has a成员 -- 有一个

使用using Base::mem; 来恢复继承成员的访问等级
eg:
class Base
{
public:
void display()
{
cout<<"Base!"<<endl;
}
void setDataMember(int i)
{
m_i=i;
}
protected:
int m_i;
};
class Derived : private Base//虽然是private继承的Base
{
public:
using Base::display;//但是可以使用 using 基类::成员; 来恢复继承成员的访问等级,但不能更宽松
using Base::setDataMember;//注意只需要使用函数名,不带参数
protected:
using Base::m_i;
};
class Derived2 : public Derived
{
public:
void getDataMember()
{
cout<<"Derived2 data member is: "<<m_i<<endl;
}
protected:
};
int main()
{
Derived2 d2;
d2.setDataMember(999);
d2.getDataMember();
char ch;
cin>>ch;
return 0;
}
struct和class的区别最大的就在于, 默认的成员保护级别默认的派生成员保护级别
struct为public,class为private
eg:
class Base{//all are private};
struct Base{//all are public};
struct Derived : Base//public Base
class Derived : Base//private Base
友元不能被继承,基类和派生类的友元各是各的
static成员,在整个继承层次中只有一个实例,同时static成员遵循访问控制符
eg:
struct Base
{
    static void staticFcn();
};
struct Derived : Base
{
    void derivedFcn(const Derived&);
};
void Derived::derivedFcn(const Derived &d)
{
    Base::staticFcn();//三个都等效
    Derived::staticFcn();
    staticFcn();
}

派生类到基类的转换

派生类对象指针/引用转换到基类类型指针/引用
如果把派生类指针/引用传递到 接受基类指针/引用的函数,则传递的是派生类
//派生类对象对基类对象进行初始化或赋值 
//可能使用到基类复制构造函数和赋值操作符
//因为一般基类里不会重载形参为派生类的复制构造函数和赋值操作符
eg:
Base(const Base &cp)
Base& Base(const Base &rhs)
//下面的重载在基类中一般不常见
Base(const Derived &cp)
Base& Base(const Derived &rhs)

对象切片

如果把派生类对象传递到 接受基类对象的函数 则传递的是派生类切片后的基类部分,也就是发生了对象切片
可访问性
派生类能不能转换成基类取决于派生列表的访问级别

基类到派生类的转换

不存在从基类到派生类的自动转换,如果确定基类可以转换成派生类,可以使用static_cast强制转换或者用dynamic_cast在运行时转换

构造函数和复制控制

构造函数和复制控制成员不能被继承

构造函数
如果只希望派生类使用基类中特殊的构造函数,那么基类中的构造函数可以声明为protected
派生类构造函数同时初始化基类
eg:
//基类构造函数
ItemBase(const std::string &book="",double salePrice=0.0):m_book(book),m_salePrice(salePrice){}
//派生类构造函数
ItemDerived(const std::string &book[=""],double salePrice[=0.0],std::size_t qty=0,double discRate=0.0):ItemBase(book,salePrice),m_qty(qty),m_discRate(discRate){}
//使用
ItemDerived derived("c++ Primer",99.0,0,0.0); //可以0-4个参数调用派生类
派生类只能初始化直接基类
eg://派生类初始化了间接基类,不正确
class Base
class Derived : public Base
class Derived2 : public Derived
Derived2(xxxx):Base(xx)这样是错误的
复制控制
如果派生类显式定义了自己的复制控制,应该完成对基类成员为派生类成员的复制控制
一般做法是显式调用基类复制控制来初始化基类部分
//派生类复制构造函数
eg:
Derived(const Derived &cp):Base(cp)//第二个cp把Derived cp转换成Base &cp来初始化基类部分
{
    //这里是Derived新成员构造
}
//派生类赋值操作符
eg:
Derived& operator=(const Derived &rhs)
{
    if(this != &rhs)
    {
        Base::operator=(rhs);
        //oth
    }
    return *this;
}
派生类析构函数

默认会先调用基类,再调用派生类,与复制控制不同

虚析构函数

因为多态的特性,可以正确的删除指针或引用调用的对象

operator=设置为虚函数

如果将operator=设置为virtual,那么每个派生类都将有一个参数为基类对象的operator=,这是没有任何意义的

派生类中原型不一致但名字相同的成员会屏蔽基类成员,如果 需要访问可以使用Base::mem来访问基类成员

虚函数在基类和派生类中原型要一致,如果在派生类中不一致,会屏蔽基类虚函数

纯虚函数

提供了可覆盖的接口,但用户不能创建该类型的对象,纯虚函数的类是抽象基类
如果该抽象基类派生出一个派生类,但是没有覆盖纯虚函数,那么该派生类也属于抽象类,不能被实例化

容器与继承

eg:
multiset<Base> base;
Base b;
Derived d;
base.insert(b);
base.insert(d);//d会被切片,解决的方法是容器保存对象指针
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值