宿迁市建设局投诉网站首页,网站开发创意想法,什么网站做前端练手好,如何查看网站关键词目录 前言010继承与派生简单例程020多级继承030使用using关键词更改访问权限040隐藏050派生类与基类成员函数同名时不构成重载060使用多级继承展示成员变量在内存中的分布情况071派生类在函数头调用基类构造函数072构造函数调用顺序080构造函数与析构函数的调用顺序091多重继承… 目录 前言010继承与派生简单例程020多级继承030使用using关键词更改访问权限040隐藏050派生类与基类成员函数同名时不构成重载060使用多级继承展示成员变量在内存中的分布情况071派生类在函数头调用基类构造函数072构造函数调用顺序080构造函数与析构函数的调用顺序091多重继承与构造函数调用顺序092访问多重继承下多个基类具有相同名称的成员变量方法093多重继承下成员变量在内存中的排列100利用指针绕开访问限制110多重继承导致的二义性120虚继承解决多重继承下的二义性130虚继承下的调用构造函数与初始化基类140虚继承下的成员变量在内存中的排序151将派生类赋值给基类_对象实例赋值152将派生类赋值给基类_对象指针赋值153将派生类赋值给基类_引用赋值160解释将派生类指针赋值给基类指针时起始位置不一致的问题 前言 本笔记所涉及到的编程环境与 《C · 代码笔记1 · 从C到C》 中的编程环境一致具体可参考此笔记。
010继承与派生简单例程 相关代码
#include iostream/*** 使用继承的两种场景* 1、基类需要扩展功能可使用继承减少重复代码。* 2、多个类具有相似功能提取出公共部分而后再进行继承。
*/// 基类声明
class Base
{
public:// 基类的构造函数Base();// 基类的析构函数~Base();// 基类的show函数用于输出信息void show(void);
};// 基类构造函数的实现输出基类构造信息
Base::Base()
{std::cout 基类构造函数被调用。 std::endl;
}// 基类析构函数的实现输出基类析构信息
Base::~Base()
{std::cout 基类析构函数被调用。 std::endl;
}// 基类show函数的实现输出基类show信息
void Base::show(void)
{std::cout 基类show函数被调用。 std::endl;
}// 派生类声明从基类Base公有继承
class Derived : public Base
{
public:// 派生类的构造函数Derived();// 派生类的析构函数~Derived();// 派生类的show函数用于输出信息void show(void);
};// 派生类构造函数的实现输出派生类构造信息
Derived::Derived()
{std::cout 派生类构造函数被调用。 std::endl;
}// 派生类析构函数的实现输出派生类析构信息
Derived::~Derived()
{std::cout 派生类析构函数被调用。 std::endl;
}// 派生类show函数的实现输出派生类show信息
void Derived::show(void)
{std::cout 派生类show函数被调用。 std::endl;
}int main(int argc, char const *argv[])
{// 创建派生类对象Derived d_obj;// 调用派生类的show函数d_obj.show();return 0;
} 运行结果 020多级继承 相关代码
#include iostream
#include string// 动物类包含名称和年龄两个成员变量
class Animal
{
public:// 动物的名称std::string m_name;// 动物的年龄int m_age;// 动物的构造函数初始化名称和年龄Animal(const std::string animalName, int animalAge) : m_name(animalName), m_age(animalAge) {}
};// 狗类继承自动物类
class Dog : public Animal
{
public:// 狗的构造函数调用动物类的构造函数初始化Dog(const std::string dogName, int dogAge) : Animal(dogName, dogAge) {}
};// 金毛犬类继承自狗类
class GoldenRetriever : public Dog
{
public:// 金毛犬的构造函数调用狗类的构造函数初始化GoldenRetriever(const std::string goldenName, int goldenAge) : Dog(goldenName, goldenAge) {}
};// 主函数
int main()
{// 创建金毛犬对象GoldenRetriever golden(金毛, 3);// 输出金毛犬的名称和年龄std::cout 金毛的名字是 golden.m_name std::endl 金毛的年龄是 golden.m_age 岁 std::endl;return 0;
} 运行结果 030使用using关键词更改访问权限 相关代码
#include iostream// 基类声明
class Base
{
public:int m_num; // 公有成员变量用于存储数字// 基类的构造函数初始化m_num为100Base() { this-m_num 100; }protected:// 基类的保护函数输出信息void privateFunction(){std::cout 这是基类的保护函数。 std::endl;}
};// 派生类声明从基类Base公有继承
class Derived : public Base
{
public:// 使用using关键字改变基类保护函数的访问权限// 从protected更改为publicusing Base::privateFunction;// 派生类的show函数用于输出m_num的值void show(void){std::cout m_num this-m_num std::endl;}private:// 使用using关键字将基类的公有成员变量m_num的访问权限更改为私有using Base::m_num;
};int main()
{Derived d; // 创建派生类对象d.privateFunction(); // 调用基类的保护函数由于using关键字这里可以公有访问// d.m_num 10; // 不能这样做因为m_num权限已经被更改成私有权限了。d.show(); // 调用派生类的show函数输出m_num的值return 0;
} 运行结果 040隐藏 相关代码
#include iostream/*** 如果派生类中的成员包括成员变量和成员函数和基类中的成员重名* 那么就会隐藏从基类继承过来的成员。* * 基类中的成员仍然可以访问不过要加上类名和域解析符
*/class Base
{
public:int m_num; // 基类的公有成员变量// 使用构造函数初始化基类的m_num值为0Base() : m_num(0) {}void show(){ // 基类的公有成员函数std::cout 基类的m_num: m_num std::endl;}
};// 派生类
class Derived : public Base
{
public:int m_num; // 派生类的公有成员变量隐藏了基类的m_num// 使用构造函数初始化派生类的m_num值为0Derived() : m_num{100} {}void show(){// 派生类的公有成员函数隐藏了基类的show函数std::cout 派生类的m_num: m_num std::endl;}
};int main()
{Derived d; // 创建派生类对象// 输出派生类的m_numstd::cout 派生类的m_num: d.m_num std::endl;// 输出基类的m_num需要显式指定std::cout 基类的m_num: d.Base::m_num std::endl;// 调用派生类的show函数d.show();// 调用基类的show函数需要显式指定d.Base::show();return 0;
} 运行结果 050派生类与基类成员函数同名时不构成重载 相关代码
#include iostream
using namespace std;/*** 一个作用域内的同名函数才具有重载关系不同作用域内的同名函数是会造成隐藏使得外层函数无效*/// 基类 Base
class Base
{
public:// 基类的函数输出基类信息void show(){cout 这是基类的 show 函数。 endl;}
};// 派生类 Derived从基类 Base 公有继承
class Derived : public Base
{
public:// 派生类中同名函数但参数不同不构成重载// 输出派生类信息void show(int num){cout 这是派生类的 show 函数参数为 num endl;}
};int main()
{Derived d; // 创建派生类对象// 调用派生类的 show 函数传递参数0d.show(0); // 输出这是派生类的 show 函数参数为0。// 显式调用基类的 show 函数d.Base::show(); // 输出这是基类的 show 函数。// 注意如果去掉注释下面的调用将会产生编译错误// 因为派生类的 show 函数隐藏了基类的 show 函数// 并且派生类的 show 函数需要一个整数参数// d.show(); // compile error// d.show(); // compile errorreturn 0;
} 运行结果 060使用多级继承展示成员变量在内存中的分布情况 相关代码
#include iostreamclass Base
{
public:int base1;int base2;Base() : base1(1), base2(2){std::cout 基类构造函数 std::endl;}
};class Derived : public Base
{
public:int derived1;int derived2;Derived() : Base(), derived1(3), derived2(4){std::cout 派生类构造函数 std::endl;}
};class MoreDerived : public Derived
{
public:int moreDerived1;int moreDerived2;MoreDerived() : Derived(), moreDerived1(5), moreDerived2(6){std::cout 更多派生类构造函数 std::endl;}
};int main()
{MoreDerived obj;// 打印对象地址和成员变量的地址std::cout 对象obj的地址: obj std::endl;std::cout 成员变量obj.base1的地址: obj.base1 std::endl;std::cout 成员变量obj.base2的地址: obj.base2 std::endl;std::cout 成员变量obj.derived1的地址: obj.derived1 std::endl;std::cout 成员变量obj.derived2的地址: obj.derived2 std::endl;std::cout 成员变量obj.moreDerived1的地址: obj.moreDerived1 std::endl;std::cout 成员变量obj.moreDerived2的地址: obj.moreDerived2 std::endl;return 0;
}运行结果 现象解释可以发现基类的成员变量排在前面派生类的排在后面。成员变量按照派生的层级依次排列新增成员变量始终在最后。 071派生类在函数头调用基类构造函数 相关代码
#include iostream/* 只能将基类构造函数的调用放在函数头部不能放在函数体中。 */// 基类Base的定义
class Base
{
private:int baseData;public:// Base类的构造函数Base(int data) : baseData(data){std::cout 基类构造函数被调用数据为: data std::endl;}// Base类的成员函数用于展示基类的数据void showBaseData(){std::cout 基类数据: baseData std::endl;}
};// 派生类Derived的定义
class Derived : public Base
{
private:int derivedData;public:// Derived类的构造函数调用基类Base的构造函数Derived(int baseData, int derivedData) : Base(baseData), derivedData(derivedData){std::cout 派生类构造函数被调用派生类数据为: derivedData std::endl;}// Derived类的成员函数用于展示派生类的数据void showDerivedData(){std::cout 派生类数据: derivedData std::endl;}
};int main()
{// 创建Derived类的对象Derived obj(10, 20);// 调用基类和派生类的成员函数obj.showBaseData();obj.showDerivedData();return 0;
} 运行结果
072构造函数调用顺序 相关代码
#include iostream/*** 定义派生类构造函数时最好指明基类构造函数* 如果不指明就调用基类的默认构造函数不带参数的构造函数* 如果没有默认构造函数那么编译失败。* */// 基类A
class A
{
public:A(){m_name ?;std::cout 调用A的默认构造函数 std::endl;}A(std::string name) : m_name(name){std::cout 调用A的有参构造函数姓名 name std::endl;}protected:std::string m_name;
};// 派生类B继承自A
class B : public A
{
public:B(){m_age 0;std::cout 调用B的默认构造函数 std::endl;}B(std::string name, int age) : A(name), m_age(age){std::cout 调用B的有参构造函数年龄 age std::endl;}protected:int m_age;
};// 派生类C继承自B
class C : public B
{
public:C(){m_score 0.0;std::cout 调用C的默认构造函数 std::endl;}C(std::string name, int age, float score) : B(name, age), m_score(score){std::cout 调用C的有参构造函数成绩 score std::endl;}public:void display(){std::cout m_name 的年龄是 m_age 成绩是 m_score 。 std::endl;}private:float m_score;
};int main()
{// 创建C类的对象使用默认构造函数C obj1;obj1.display();// 创建C类的对象使用有参构造函数C obj2(std::string(小明), 16, 90.5);obj2.display();return 0;
} 运行结果 080构造函数与析构函数的调用顺序 相关代码
#include iostream
using namespace std;class A
{
public:A() { cout A构造函数 endl; }~A() { cout A析构函数 endl; }
};class B : public A
{
public:B() { cout B构造函数 endl; }~B() { cout B析构函数 endl; }
};class C : public B
{
public:C() { cout C构造函数 endl; }~C() { cout C析构函数 endl; }
};int main(int argc, char const *argv[])
{C test;return 0;
} 运行结果 091多重继承与构造函数调用顺序 相关代码
#include iostream
using namespace std;/** 基类构造函数的调用顺序和和它们在派生类构造函数中出现的顺序无关* 而是和[声明]派生类时基类出现的顺序相同。 * */// 基类BaseB的定义
class BaseB
{
public:// BaseB的构造函数接受两个整型参数BaseB(int c, int d);// BaseB的析构函数~BaseB();protected:// BaseB的 保护成员变量int m_c;int m_d;
};
// BaseB的构造函数实现初始化成员变量
BaseB::BaseB(int c, int d) : m_c(c), m_d(d)
{cout BaseB构造函数 endl;
}
// BaseB的析构函数实现
BaseB::~BaseB()
{cout BaseB析构函数 endl;
}// 基类BaseA的定义
class BaseA
{
public:// BaseA的构造函数接受两个整型参数BaseA(int a, int b);// BaseA的析构函数~BaseA();protected:// BaseA的 保护成员变量int m_a;int m_b;
};
// BaseA的构造函数实现初始化成员变量
BaseA::BaseA(int a, int b) : m_a(a), m_b(b)
{cout BaseA构造函数 endl;
}
// BaseA的析构函数实现
BaseA::~BaseA()
{cout BaseA析构函数 endl;
}// 派生类Derived的定义继承自BaseA和BaseB
class Derived : public BaseB, public BaseA
{
public:// Derived的构造函数接受五个整型参数Derived(int a, int b, int c, int d, int e);// Derived的析构函数~Derived();public:// Derived的公共成员函数用于显示成员变量的值void show();private:// Derived的 私有成员变量int m_e;
};
// Derived的构造函数实现初始化基类和派生类的成员变量
Derived::Derived(int a, int b, int c, int d, int e) : BaseB(c, d), BaseA(a, b), m_e(e)
{cout Derived构造函数 endl;
}
// Derived的析构函数实现
Derived::~Derived()
{cout Derived析构函数 endl;
}
// Derived的show成员函数实现输出成员变量的值
void Derived::show()
{cout m_a , m_b , m_c , m_d , m_e endl;
}// 程序的入口点
int main()
{// 创建Derived类的对象并传入初始化参数Derived obj(1, 2, 3, 4, 5);// 调用obj的show函数输出成员变量的值obj.show();// 程序结束返回0return 0;
} 运行结果 092访问多重继承下多个基类具有相同名称的成员变量方法 相关代码
#include iostream
using namespace std;/** 当两个或多个基类中有同名的成员时* 要在成员名字前面加上类名和域解析符::* 以显式地指明到底使用哪个类的成员消除二义性。*/// 基类A
class A
{
public:void show(){cout A::show() 被调用 endl;}
};// 基类B
class B
{
public:void show(){cout B::show() 被调用 endl;}
};// 派生类C继承自A和B
class C : public A, public B
{
};int main()
{// 创建C类的对象C obj;// 调用A类的show函数obj.A::show(); // 通过类名和域解析符显式指定调用A类的show函数// 调用B类的show函数obj.B::show(); // 通过类名和域解析符显式指定调用B类的show函数return 0;
} 运行结果 093多重继承下成员变量在内存中的排列 相关代码
#include iostream// 基类A
class A
{
public:int a1;int a2;A() : a1(1), a2(2){std::cout A类构造函数 std::endl;}
};// 基类B
class B
{
public:int b1;int b2;B() : b1(3), b2(4){std::cout B类构造函数 std::endl;}
};// 基类C
class C
{
public:int c1;int c2;C() : c1(5), c2(6){std::cout C类构造函数 std::endl;}
};// 派生类D继承自A、B和C
class D : public A, public B, public C
{
public:D(){std::cout D类构造函数 std::endl;}
};int main()
{// 创建D类的对象D obj;// 输出成员变量的地址std::cout 地址 of obj.a1: obj.a1 std::endl;std::cout 地址 of obj.a2: obj.a2 std::endl;std::cout 地址 of obj.b1: obj.b1 std::endl;std::cout 地址 of obj.b2: obj.b2 std::endl;std::cout 地址 of obj.c1: obj.c1 std::endl;std::cout 地址 of obj.c2: obj.c2 std::endl;return 0;
} 运行结果 现象解释事实上多重继承的成员变量也是按照派生类的继承声明顺序来排列的。 100利用指针绕开访问限制 相关代码
#include iostreamclass MyClass
{
private:int privateData;protected:int protectedData;public:// 初始化私有和保护成员变量MyClass() : privateData(10), protectedData(20) {}
};int main()
{MyClass obj;// 获取对象的地址void *objPtr obj;// 计算私有成员变量的偏移量// 假设int类型的大小为4字节size_t privateDataOffset 0; // 假设私有成员变量在类中的第一个位置// 计算保护成员变量的偏移量// 假设保护成员变量在私有成员变量之后size_t protectedDataOffset sizeof(int); // 私有成员变量之后// 创建指向私有成员变量的指针int *privateDataPtr (int *)((char *)objPtr privateDataOffset);// 创建指向保护成员变量的指针int *protectedDataPtr (int *)((char *)objPtr protectedDataOffset);// 通过指针访问私有成员变量std::cout 私有成员变量的值: *privateDataPtr std::endl;// 通过指针访问保护成员变量std::cout 保护成员变量的值: *protectedDataPtr std::endl;return 0;
} 运行结果 110多重继承导致的二义性 相关代码
#include iostream// 间接基类A
class A
{
protected:int m_a;
};// 直接基类B
class B : public A
{
protected:int m_b;
};// 直接基类C
class C : public A
{
protected:int m_c;
};// 派生类D
class D : public B, public C
{
public:// 命名冲突程序不知道是哪条继承路径下的m_avoid seta(int a) { m_a a; }void setb(int b) { m_b b; }void setc(int c) { m_c c; }void setd(int d) { m_d d; }private:int m_d;
};int main()
{D d;return 0;
}运行结果 解释由于代码有二义性编译器直接不给通过编译。
120虚继承解决多重继承下的二义性 相关代码
#include iostream// 间接基类A
class A
{
protected:int m_a;
};// 直接基类B虚继承
class B : virtual public A
{
protected:int m_b;
};// 直接基类C虚继承
class C : virtual public A
{
protected:int m_c;
};// 派生类D
class D : public B, public C
{
public:void seta(int a) { m_a a; }void setb(int b) { m_b b; }void setc(int c) { m_c c; }void setd(int d) { m_d d; }private:int m_d;
};int main()
{D d;return 0;
}运行结果 130虚继承下的调用构造函数与初始化基类 相关代码
#include iostream// 虚基类A的声明和定义
/*** brief 虚基类A提供基本的属性m_a*/
class A
{
public:// A类的构造函数/*** brief 构造函数初始化m_a* param a 初始化m_a的值*/A(int a);protected:// A类的保护成员存储构造函数中传入的值int m_a;
};
// A类的构造函数定义
A::A(int a) : m_a(a) {}// 直接派生类B的声明和定义
/*** brief 直接派生类B从虚基类A派生增加新的属性m_b*/
class B : virtual public A
{
public:// B类的构造函数/*** brief 构造函数初始化m_a和m_b* param a 初始化虚基类A的m_a* param b 初始化自己的m_b*/B(int a, int b);public:// B类的成员函数用于显示成员变量的值/*** brief 显示m_a和m_b的值*/void display();protected:// B类的保护成员存储构造函数中传入的值int m_b;
};
// B类的构造函数定义
B::B(int a, int b) : A(a), m_b(b) {}
// B类的成员函数定义
void B::display()
{std::cout m_a m_a , m_b m_b std::endl;
}// 直接派生类C的声明和定义
/*** brief 直接派生类C从虚基类A派生增加新的属性m_c*/
class C : virtual public A
{
public:// C类的构造函数/*** brief 构造函数初始化m_a和m_c* param a 初始化虚基类A的m_a* param c 初始化自己的m_c*/C(int a, int c);public:// C类的成员函数用于显示成员变量的值/*** brief 显示m_a和m_c的值*/void display();protected:// C类的保护成员存储构造函数中传入的值int m_c;
};
// C类的构造函数定义
C::C(int a, int c) : A(a), m_c(c) {}
// C类的成员函数定义
void C::display()
{std::cout m_a m_a , m_c m_c std::endl;
}// 间接派生类D的声明和定义
/*** brief 间接派生类D从B和C派生增加新的属性m_d*/
class D : public B, public C
{
public:// D类的构造函数/*** brief 构造函数初始化m_a、m_b、m_c和m_d* param a 初始化虚基类A的m_a* param b 初始化B类的m_b* param c 初始化C类的m_c* param d 初始化自己的m_d*/D(int a, int b, int c, int d);public:// D类的成员函数用于显示成员变量的值/*** brief 显示m_a、m_b、m_c和m_d的值*/void display();private:// D类的私有成员存储构造函数中传入的值int m_d;
};
// D类的构造函数定义
/* 最终的派生类 D 来初始化虚基类 A直接派生类 B 和 C 对 A 的构造函数的调用是无效的。 */
D::D(int a, int b, int c, int d) : A(a), B(90, b), C(100, c), m_d(d) {}
// D类的成员函数定义
void D::display()
{std::cout m_a m_a , m_b m_b , m_c m_c , m_d m_d std::endl;
}// 主函数
/*** brief 程序的入口点* return int 返回状态码*/
int main()
{// 创建B类对象b并调用display函数B b(10, 20);b.display();// 创建C类对象c并调用display函数C c(30, 40);c.display();// 创建D类对象d并调用display函数D d(50, 60, 70, 80);d.display();return 0;
} 运行结果 140虚继承下的成员变量在内存中的排序 相关代码
#include iostreamclass A
{
public:int a_num1;int a_num2;
};class B : virtual public A
{
public:int b_num1;int b_num2;
};class C : virtual public A
{
public:int c_num1;int c_num2;
};class D : public B, public C
{
public:int d_num1;int d_num2;
};int main(int argc, char const *argv[])
{D d;// 打印D对象中各个成员变量的地址std::cout d.a_num1 的地址: d.a_num1 std::endl;std::cout d.a_num2 的地址: d.a_num2 std::endl;std::cout d.b_num1 的地址: d.b_num1 std::endl;std::cout d.b_num2 的地址: d.b_num2 std::endl;std::cout d.c_num1 的地址: d.c_num1 std::endl;std::cout d.c_num2 的地址: d.c_num2 std::endl;std::cout d.d_num1 的地址: d.d_num1 std::endl;std::cout d.d_num2 的地址: d.d_num2 std::endl;return 0;
} 运行结果 对于虚继承将派生类分为固定部分和共享部分并把共享部分虚继承的基类放在最后。 151将派生类赋值给基类_对象实例赋值 相关代码
#include iostream/*** 对象之间的赋值是成员变量的赋值成员函数不存在赋值问题。* 对象之间的赋值不会影响成员函数也不会影响 this 指针。*//*** class A* brief 定义了一个简单的类A具有一个成员变量m_a和一个display函数。*/
class A
{
public:/*** brief A类的构造函数用于初始化成员变量m_a。* param a 初始化m_a的值。*/A(int a);public:/*** brief 打印类A的成员变量m_a的值。*/void display();public:int m_a; /// 类A的成员变量存储整数类型的数据。
};
// A类的构造函数定义使用成员初始化列表初始化m_a。
A::A(int a) : m_a(a) {}
// display函数定义输出m_a的值。
void A::display()
{std::cout Class A: m_a m_a std::endl;
}/*** class B* brief 定义了一个继承自A的类B添加了成员变量m_b和一个重写的display函数。*/
class B : public A
{
public:/*** brief B类的构造函数调用基类A的构造函数初始化m_a并初始化成员变量m_b。* param a 初始化基类A的成员变量m_a的值。* param b 初始化成员变量m_b的值。*/B(int a, int b);public:/*** brief 重写基类A的display函数打印类B的成员变量m_a和m_b的值。*/void display();public:int m_b; /// 类B的成员变量存储整数类型的数据。
};
// B类的构造函数定义首先调用基类A的构造函数然后使用成员初始化列表初始化m_b。
B::B(int a, int b) : A(a), m_b(b) {}
// display函数定义输出m_a和m_b的值。
void B::display()
{std::cout Class B: m_a m_a , m_b m_b std::endl;
}/*** brief 主函数程序入口。* param argc 命令行参数的数量。* param argv 命令行参数的字符串数组。* return 程序执行状态码。*/
int main(int argc, char const *argv[])
{A a(10); // 创建A类对象a并初始化m_a为10。B b(66, 99); // 创建B类对象b并初始化m_a为66m_b为99。std::cout 赋值前的数据 std::endl;a.display(); // 调用a的display函数。b.display(); // 调用b的display函数。std::cout std::endl;std::cout 赋值后的数据 std::endl;a b; // 将b的值赋给a这里会发生切片只复制基类A的部分。a.display(); // 调用a的display函数此时a的m_a被b的m_a覆盖。b.display(); // 调用b的display函数b的值未改变。return 0; // 程序执行成功返回0。
} 运行结果 152将派生类赋值给基类_对象指针赋值 相关代码
#include iostream/*** 编译器通过指针来访问成员变量指针指向哪个对象就使用哪个对象的数据* 编译器通过指针的类型来访问成员函数指针属于哪个类的类型就使用哪个类的函数。*/// 基类A的定义包含一个int类型的成员变量m_a
class A
{
public:// A类的构造函数初始化m_aA(int a);public:// 显示m_a的值的成员函数void display();protected:// 保护成员用于存储A类的数据int m_a;
};
// A类构造函数的实现初始化m_a
A::A(int a) : m_a(a) {}
// display函数的实现打印m_a的值
void A::display()
{std::cout Class A: m_a m_a std::endl;
}// 派生类B的定义从A类公有继承并增加一个int类型的成员变量m_b
class B : public A
{
public:// B类的构造函数初始化m_a和m_bB(int a, int b);public:// 重写的display函数打印m_a和m_b的值void display();protected:// 保护成员用于存储B类特有的数据int m_b;
};
// B类构造函数的实现调用基类A的构造函数并初始化m_b
B::B(int a, int b) : A(a), m_b(b) {}
// display函数的实现打印m_a和m_b的值
void B::display()
{std::cout Class B: m_a m_a , m_b m_b std::endl;
}// 类C的定义包含一个int类型的成员变量m_c
class C
{
public:// C类的构造函数初始化m_cC(int c);public:// 显示m_c的值的成员函数void display();protected:// 保护成员用于存储C类的数据int m_c;
};
// C类构造函数的实现初始化m_c
C::C(int c) : m_c(c) {}
// display函数的实现打印m_c的值
void C::display()
{std::cout Class C: m_c m_c std::endl;
}// 派生类D的定义公有继承自B和C类并增加一个int类型的成员变量m_d
class D : public B, public C
{
public:// D类的构造函数初始化m_a, m_b, m_c和m_dD(int a, int b, int c, int d);public:// 重写的display函数打印m_a, m_b, m_c和m_d的值void display();private:// 私有成员用于存储D类特有的数据int m_d;
};
// D类构造函数的实现调用基类B和C的构造函数并初始化m_d
D::D(int a, int b, int c, int d) : B(a, b), C(c), m_d(d) {}
// display函数的实现打印m_a, m_b, m_c和m_d的值
void D::display()
{std::cout Class D: m_a m_a , m_b m_b , m_c m_c , m_d m_d std::endl;
}
// 主函数
int main()
{// 创建A类的对象A *pa new A(1);// 创建B类的对象B *pb new B(2, 20);// 创建C类的对象C *pc new C(3);// 创建D类的对象D *pd new D(4, 40, 400, 4000);// 将pd地址赋给papa此时指向D类的对象pa pd;// 调用pa指向对象的display函数pa-display();// 将pd地址赋给pbpb此时指向D类的对象pb pd;// 调用pb指向对象的display函数pb-display();// 将pd地址赋给pcpc此时指向D类的对象pc pd;// 调用pc指向对象的display函数pc-display();// 打印各个指针的地址std::cout ----------------------- std::endl;std::cout pa pa std::endl;std::cout pb pb std::endl;std::cout pc pc std::endl;std::cout pd pd std::endl;// 删除pd指向的对象delete pd;// 程序结束return 0;
} 运行结果 pc的起始地址从这里看出地址与其他对象不一致这在后面再解释但可以看出将派生类指针赋值给基类指针。与对象变量之间的赋值不同的是对象指针之间的赋值并没有拷贝对象的成员也没有修改对象本身的数据仅仅是改变了指针的指向。
153将派生类赋值给基类_引用赋值 相关代码
#include iostream/*** 编译器通过指针来访问成员变量指针指向哪个对象就使用哪个对象的数据* 编译器通过指针的类型来访问成员函数指针属于哪个类的类型就使用哪个类的函数。* * 引用的底层也是指针所以行为跟指针相似*/// 基类A的定义包含一个int类型的成员变量m_a
class A
{
public:// A类的构造函数初始化m_aA(int a);public:// 显示m_a的值的成员函数void display();protected:// 保护成员用于存储A类的数据int m_a;
};
// A类构造函数的实现初始化m_a
A::A(int a) : m_a(a) {}
// display函数的实现打印m_a的值
void A::display()
{std::cout Class A: m_a m_a std::endl;
}
// 派生类B的定义从A类公有继承并增加一个int类型的成员变量m_b
class B : public A
{
public:// B类的构造函数初始化m_a和m_bB(int a, int b);public:// 重写的display函数打印m_a和m_b的值void display();protected:// 保护成员用于存储B类特有的数据int m_b;
};
// B类构造函数的实现调用基类A的构造函数并初始化m_b
B::B(int a, int b) : A(a), m_b(b) {}
// display函数的实现打印m_a和m_b的值
void B::display()
{std::cout Class B: m_a m_a , m_b m_b std::endl;
}
// 类C的定义包含一个int类型的成员变量m_c
class C
{
public:// C类的构造函数初始化m_cC(int c);public:// 显示m_c的值的成员函数void display();protected:// 保护成员用于存储C类的数据int m_c;
};
// C类构造函数的实现初始化m_c
C::C(int c) : m_c(c) {}
// display函数的实现打印m_c的值
void C::display()
{std::cout Class C: m_c m_c std::endl;
}
// 派生类D的定义公有继承自B和C类并增加一个int类型的成员变量m_d
class D : public B, public C
{
public:// D类的构造函数初始化m_a, m_b, m_c和m_dD(int a, int b, int c, int d);public:// 重写的display函数打印m_a, m_b, m_c和m_d的值void display();private:// 私有成员用于存储D类特有的数据int m_d;
};
// D类构造函数的实现调用基类B和C的构造函数并初始化m_d
D::D(int a, int b, int c, int d) : B(a, b), C(c), m_d(d) {}
// display函数的实现打印m_a, m_b, m_c和m_d的值
void D::display()
{std::cout Class D: m_a m_a , m_b m_b , m_c m_c , m_d m_d std::endl;
}int main(int argc, char const *argv[])
{D d(4, 40, 400, 4000);A ra d;B rb d;C rc d;ra.display();rb.display();rc.display();return 0;
}运行结果 160解释将派生类指针赋值给基类指针时起始位置不一致的问题 相关代码
#include iostreamclass A
{
public:int a;
};class B : public A
{
public:int b;
};class C
{
public:int c;
};class D : public B, public C
{
public:int d;
};class E : public C, public B
{
public:int e;
};int main(int argc, char const *argv[])
{D *pd new D;std::cout 派生类 D 指针赋值给各基类情况 std::endl;A *pa pd;std::cout pa pa std::endl;B *pb pd;std::cout pb pb std::endl;C *pc pd;std::cout pc pc std::endl;std::cout pd pd std::endl;std::cout std::endl;E *pe new E;std::cout 派生类 E 指针赋值给各基类情况 std::endl;pa pe;std::cout pa pa std::endl;pb pe;std::cout pb pb std::endl;pc pe;std::cout pc pc std::endl;std::cout pe pe std::endl;delete pd;return 0;
} 运行结果 现象解释事实上派生类D和派生类E的继承结构如下所示 类D和类E不一样的地方在于声明继承顺序的时候不一样。 对于类D
class D : public B, public C对于类E
class E : public C, public B从上可输出结果可看出继承顺序声明不一样这导致成员变量在内存中的排序也不一样如下如图所示 从上可看出成员变量内存地址并没有顺序递增这个情况是受到了继承顺序和编译器实现的影响。