继承机制
是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员保持原有类特性的基础上进行扩展,增加功能;这样产生新的类,称派生类;继承呈现了面向对象程序设计的层次结构;体现了由简单到复杂的认识过程
多态性
是考虑在不同层次的类中,以及在统一类中,统么的成员函数之间的关系问题。函数的重载,运算符的重载,属于编译时的多态性;以虚基类为基础的运行时的多态性是面向对象程序设计的标志性特征;体现了类推和比喻的思想方法
早期绑定:编译时就确定了调用关系
晚绑定:程序运行的过程中才能确定调用关系
层次概念是计算机的重要概念
通过继承(inheritance)的机制课对类(class)分层,提供类型/子类型的关系
C++通过类派生(class derivation)的机制来支持继承;被继承的类称为基类(base class)或超类(superclass),新产生的类为派生类(derived class)或子类(subclass);基类的派生类的集合称作类继承层次结构(hierarchy)
由基类派生出派生类的定义的一般形式为:
class 派生类名:访问限定符 基类名1,访问限定符 积累名2,…访问限定符 基类型n
{
private:
成员表1; //派生类增加或替代的私有成员
public:
成员表2; //派生类增加或替代的共有成员
protected:
成员表3; //派生类增加或替代的保护成员
}
class Object
{
public:
int value;
int num;
public:
Object(int x = 0,int y = 0) :value(x), num(y)
{
cout << "create Object:" << this << endl;
}
};
class Base :public Object
{
public:
int sum;
int fib;
public:
Base(int a = 0, int b = 0) :sum(a), fib(b)
{
cout << "create Base:" << this << endl;
}
};
int main()
{
Base base;
}
在我们创建base对象时,首先到达派生类的构造函数,但是并不构造派生类对象,而是先构建Object对象,随后在构建派生类对象
也就是说base有三个成员,分别是:隐藏基对象、sum、fib
再看下面的例子
class A
{
private:
int ax;
protected:
int ay;
public:
int az;
public:
A()
{
ax = ay = az = 0;
}
};
class B :public A
{
private:
int bx;
protected:
int by;
public:
int bz;
public:
B()
{
bx = by = bz = 1;
}
void fun()
{
ax = 10; //error
ay = 20;
az = 30;
}
};
int main()
{
B b;
b.fun();
}
由于ax
属于基类的私有成员,所以在b.fun()
中 ax = 10;
,是错误的,也就是非自己的私有无法访问
若我们将上面的共有继承更改为私有继承
所以,无论任何继承关系,子类对象的方法可以访问隐藏基类中的公有属性与保护属性
当我们添加私有属性的有名A类对象,,更加体现了保护属性对继承对象与具名对象的差别
我们接着看下面,在main函数在的访问
如果我们将其改为私有继承关系,那么我们将无法从外部继续访问 b.az = 100
三层关系
class A
{
private:
int ax;
protected:
int ay;
public:
int az;
};
class B :private A
{
private:
int bx;
protected:
int by;
public:
int bz;
};
class C :public B
{
private:
int cx;
protected:
int cy;
public:
int cz;
};
int main()
{
C c;
}
可以看到其中的嵌套关系,C中包含B,B中包含A,无论是私有继承、公有继承或保护继承,所产生对象的内存分布是相同的
并且B私有继承A,继而B对象可以访问ay,az;而C公有继承B,C是无法去访问B对象中的私有属性的,也就是无法访问私有继承的A基类
隐藏基类同名成员
class A
{
protected:
int ax;
public:
A() :ax(0) {}
void fun(int x)
{
cout << "A:fun" << value << endl;
}
};
class B : public A
{
private:
int ax;
public:
B():ax(10){}
void fun()
{
ax = 100;
}
};
int main()
{
B b;
b.fun();
// b.fun(10); 错误!!!
}
若类中的成员与隐藏基类的成员重名,也就是当我们B的对象访问ax时,会根据就近原则去访问B类的ax
不仅仅是成员变量,包括成员方法同样会有同名隐藏,若想要调用被隐藏的成员则需要加上作用域A::ax = 10; A::fun(10);
在任何需要基类对象的地方都可以用公有派生类的对象来代替,这条规则称为赋值兼容规则,它包括以下情况:
C++面向对象编程中一条重要的规则是:公有继承意味着“是一个”,一定牢记这条规则
class Object
{
private:
int value;
public:
Object(int x=0):value(x)
{
cout << "Create Object:" << this << endl;
}
~Object()
{
cout << "Destroy Object:" << this << endl;
}
};
class Base :public Object
{
private:
int num;
public:
Base(int x) :num(x), Object(x + 10)
{
cout << "Create Base:" << this << endl;
}
~Base()
{
cout << "Destroy Base:" << this << endl;
}
};
int main()
{
Object obja(100);
Base base(10);
obja = base;
Object* op = &base;
Object& obj = base;
return 0;
}
obja = base;
将派生类赋值给基类,会产生切片效应,赋值兼容性;且必须是公有继承
Object* op = &base;
op仅能base空间中的隐藏Object类,op的类型会进行约束,仅能看到Object类型大小的地址;也就是说只能识别出value,而无法识别出num,引用与指针同理
接着看下面的例子
int main()
{
Base base(10);
return 0;
}
可以看到,首先构建基类对象,再进行构建派生类对象
对基类进行拷贝构造:
class Object
{
private:
int value;
public:
Object(int x=0):value(x)
{
cout << "Create Object:" << this << endl;
}
~Object()
{
cout << "Destroy Object:" << this << endl;
}
Object(const Object& obj) :value(obj.value)
{
cout << "Copy Create Object:" << this << endl;
}
};
class Base :public Object
{
private:
int num;
public:
Base(int x) :num(x), Object(x + 10)
{
cout << "Create Base:" << this << endl;
}
~Base()
{
cout << "Destroy Base:" << this << endl;
}
Base(const Base& base) :num(base.num)
{
cout << "Copy Create Base:" << this << endl;
}
};
int main()
{
Base base(10);
Base base2(base);
return 0;
}
拷贝构造不具有继承性
我们可以看到,对于base2,其子类型是调用拷贝构造,而父类型是调用构造,若我们将父类型的构造函数修改为无默认值,则编译无法通过
若想要去调用父类的拷贝构造进行拷贝:
Base(const Base& base) :num(base.num),Object(base)
{
cout << "Copy Create Base:" << this << endl;
}
通过赋值兼容性规则,将子类型给到父类型的引用,从而去调用拷贝构造
同样赋值语句也不具有继承性
class Object
{
private:
int value;
public:
Object(int x=0):value(x)
{
cout << "Create Object:" << this << endl;
}
~Object()
{
cout << "Destroy Object:" << this << endl;
}
Object(const Object& obj) :value(obj.value)
{
cout << "Copy Create Object:" << this << endl;
}
Object& operator=(const Object& obj)
{
if (this != &obj)
{
value = obj.value;
}
cout << "Object::operator=()" << endl;
return *this;
}
};
class Base :public Object
{
private:
int num;
public:
Base(int x) :num(x), Object(x + 10)
{
cout << "Create Base:" << this << endl;
}
~Base()
{
cout << "Destroy Base:" << this << endl;
}
Base(const Base& base) :num(base.num),Object(base)
{
cout << "Copy Create Base:" << this << endl;
}
Base& operator=(const Base& base)
{
if (this != &base)
{
num = base.num;
}
cout << "Base::operator=():" << endl;
return *this;
}
};
int main()
{
Base basea(10);
Base baseb(20);
basea = baseb;
return 0;
}
可以看到,我们仅赋值了num,而没有赋值value;这是因为无法再子类中调动夫类的赋值函数
在这里只需要下面的操作:
Base& operator=(const Base& base)
{
if (this != &base)
{
*(Object*)this = base;
num = base.num;
}
cout << "Base::operator=():" << endl;
return *this;
}
将this指针强转为父类型,这样就可以调用父类方法