html5企业网站,平面设计是什么专业学的,厦门网站优化建设,动完网站设计网站1.继承的概念和定义
1.1继承的概念 继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段#xff0c;它允许程序员在 保 持原有类特性的基础上进行扩展 #xff0c;增加功能#xff0c;这样产生新的类#xff0c;称派生类。继承 呈现了面向对象 程序…1.继承的概念和定义
1.1继承的概念 继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段它允许程序员在 保 持原有类特性的基础上进行扩展 增加功能这样产生新的类称派生类。继承 呈现了面向对象 程序设计的层次结构 体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用 继 承是类设计层次的复用。 #include iostream
class A
{
public:int ma;
private:int mb;
protected:int mc;
};class B : public A // 继承A叫做基类/父类 B叫派生类/子类
{
public:void func(){std::cout ma std::endl;}int md;
private:int me;
protected:int mf;
};int main()
{A a;B b;std::cout sizeof(a) std::endl; // 12std::cout sizeof(b) std::endl; // 24
}
1.2继承基类成员访问方式的变化 总结 1.基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私 有成员还是被继承到了派生类对象中但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它。 2. 基类private成员在派生类中是不能被访问如果基类成员不想在类外直接被访问但需要在 派生类中能访问就定义为protected。可以看出保护成员限定符是因继承才出现的。 3. 实际上面的表格我们进行一下总结会发现基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 Min(成员在基类的访问限定符继承方式)public protected private。 4. 使用关键字class时默认的继承方式是private使用struct时默认的继承方式是public不过 最好显示的写出继承方式。 5. 在实际运用中一般使用都是public继承几乎很少使用protetced/private继承也不提倡 使用protetced/private继承因为protetced/private继承下来的成员都只能在派生类的类里 面使用实际中扩展维护性不强 1.3默认的继承方式
class定义的默认privatestruct定义的默认public的.
1.4派生类的构造函数
派生类可以继承基类的构造函数和析构函数用来初始化和释放从基类继承来的成员变量 派生类的构造函数和析构函数负责初始化和清理派生类 派生来从基类继承来的成员的初始化和清理由基类的构造函数和析构函数来负责。
2.基类和派生类对象赋值转换
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片 或者切割。寓意把派生类中父类那部分切来赋值过去。 基类对象不能赋值给派生类对象。 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类 的指针是指向派生类对象时才是安全的。这里基类如果是多态类型可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。
#include iostream
/*
1.把继承结构也说成从上基类到下派生类的结构
2.基类对象 - 派生类对象派生类对象 - 基类对象基类指针引用 - 派生类对象派生类指针引用 - 基类对象总结在继承结构中进行上下的类型转换默认支持从下到上的类型的转换
*/class Base
{
public:Base(int data 10) :ma(data){}void show(){std::cout Base:show() std::endl;}void show(int){std::cout Base:show(int) std::endl;}
private:int ma;
};class Derive : public Base
{
public:Derive(int data 20):Base(data), mb(data){}void show(){std::cout Derivr::show() std::endl;}
private:int mb;
};int main()
{Base b(10);Derive d(20);// 基类对象 - 派生类对象 类型从下到上的转换 Yb d;// 派生类对象 - 基类对象 类型从上到下的转换 N// d b;// 基类指针引用- 派生类对象 类型从下到上的转换 Y// 只能访问派生类继承的基类的那部分内容Base* pb d;pb-show();pb-show(10);((Derive*)pb)-show(); // 类型强转为派生类的指针就可以访问派生类的内容// 派生类指针引用- 基类对象 类型从上到下的转换 N/*Derive* pd (Derive*) b; 不安全涉及了内存的非法访问pd-show();*/d.show();d.Base::show(10);// d.show(20); // 优先找的是派生类自己作用域的show名字成员没有的话才去基类里面找return 0;
}
3.重载、隐藏、覆盖 1.重载关系 一组函数要重载必须处在同一个作用域中而且函数名字相同参数列表不同 2.隐藏关系 在继承结构当中派生类的同名成员把基类的同名成员给隐藏调用了 3.覆盖关系/相当于重写方法 虚函数表中虚函数地址的覆盖
4.虚函数、静态绑定、动态绑定
#include iostream
#include typeinfoclass Base
{
public:Base(int data 10) :ma(data){}// 虚函数virtual void show(){std::cout Base:show() std::endl;}// 虚函数virtual void show(int){std::cout Base:show(int) std::endl;}
private:int ma;
};class Derive : public Base
{
public:Derive(int data 20) :Base(data), mb(data){}void show(){std::cout Derivr::show() std::endl;}
private:int mb;
};int main()
{Derive d(50);Base* pb d;/*pb是基类类型 Base::show如果发现show是普通函数就进行静态绑定call Base::show()如果发现pb是基类类型编译阶段在Base类中去看show函数发现是虚函数就进行动态绑定了*/pb-show(); // 静态编译时期的绑定函数的调用pb-show(10);std::cout sizeof(Base) std::endl;std::cout sizeof(Derive) std::endl;/*pb的类型Base - 有没有虚函数如果Base没有虚函数*pb识别的就是编译时期的类型 *pb - Base类型如果Base有虚函数*pb识别的就是运行时期的类型 RTTI 类型 calss Derive*/std::cout typeid(pb).name() std::endl;std::cout typeid(*pb).name() std::endl;return 0;
} 总结一 如果一个类里面定义了虚函数那么编译阶段编译器给这个类类型产生了一个唯一的vftable虚函数表 虚函数表中主要存储的内容就是RTTI指针和虚函数的地址。 RTTIrun-time type information 运行时的类型信息 指向类型字符串例如我的类是Base那么RTTI指向Base 当程序运行时每一张虚函数表都会加载到内存的.rodata区是个只读数据区也叫常量区。 总结二 一个类里面定义了虚函数那么这个类定义的对象。其运行时内存中开始部分多存储一个vfptr虚函数指针指向 类型的虚函数表vftable。一个类型定义的n个对象它们的vfptr指向的都是同一张虚函数表。 总结三 一个类里面虚函数的个数不影响对象内存大小都是多一个虚函数指针vfptr 4个字节影响的是虚函数表的大小 总结四 如果中的方法和基类继承来的某个方法返回值函数名参数列表都相同而且基类的方法是virtual虚函数那么派生类的这个方法自动处理成虚函数。这两个函数的关系是覆盖的关系相当于重写了这个函数 5.派生类的默认成员函数 6 个默认成员函数 “ 默认 ” 的意思就是指我们不写编译器会变我们自动生成一个那么在派生类中这几个成员函数是如何生成的呢 1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数则必须在派生类构造函数的初始化列表阶段显示调用。 2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。 3. 派生类的 operator 必须要调用基类的 operator 完成基类的复制。 4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。 5. 派生类对象初始化先调用基类构造再调派生类构造。 6. 派生类对象析构清理先调用派生类析构再调基类的析构。 7. 因为后续一些场景析构函数需要构成重写重写的条件之一是函数名相同 ( 这个我们后面会讲解) 。那么编译器会对析构函数名进行特殊处理处理成 destrutor() 所以父类析构函数不加virtual的情况下子类析构函数和父类析构函数构成隐藏关系。 6.继承和友元 友元关系不能继承 也就是说基类友元不能访问子类私有和保护成员 class Student;
class Person
{
public:friend void Display(const Person p, const Student s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 学号
};
void Display(const Person p, const Student s)
{cout p._name endl;cout s._stuNum endl;
}
void main()
{Person p;Student s;Display(p, s);
} 7.继承与静态成员 基类定义了 static 静态成员则整个继承体系里面只有一个这样的成员 。无论派生出多少个子 类都只有一个 static 成员实例 。 class Person
{
public :Person () { _count ;}
protected :string _name ; // 姓名
public :static int _count; // 统计人的个数。
};
int Person :: _count 0;
class Student : public Person
{
protected :int _stuNum ; // 学号
};
class Graduate : public Student
{
protected :string _seminarCourse ; // 研究科目
};
void TestPerson()
{Student s1 ;Student s2 ;Student s3 ;Graduate s4 ;cout 人数 : Person ::_count endl;Student ::_count 0;cout 人数 : Person ::_count endl;
}