当前位置: 首页 > news >正文

c++ 继承

继承的概念

总结:继承的本质就是复用,集成的作用就是减少重复代码

 例如有羊,猫,狗三个动物.每个动物都有性别,年龄等属性.都有行走,睡觉等方法.我们可以为每个动物写一个类,如下:

class sheep
{
public:void work(){}void sleep(){}
private:int _age;int _sex;
};
class dog
{
public:void work(){}void sleep(){}
private:int _age;int _sex;
};class cat
{
public:void work(){}void sleep(){}
private:int _age;int _sex;
};

我们发现其中大部分东西都是重复的,我们可以将其公用的属性和方法封装到一个类里面,然后其他的类都复用这个类即可:

class Animal
{
public:void work(){}void sleep(){}
private:int _age;int _sex;
};
class sheep: public Animal{};
class dog: public Animal{};
class cat: public Animal{};

继承的三种方式

继承有三种继承方式:public,protect,private.

通过观察下面代码我们可以总结规律:

sleep() 方法在 Animal 类中是 protected 的,这意味着它只能在 Animal 类本身、Animal 的派生类(如 sheepdogcat)内部,以及这些派生类的友元函数中被访问。在派生类的外部(如 main 函数中),sleep() 是不可见的,无论是通过 sheepdog 还是 cat 的实例。

此外,尝试直接访问 _age 也是错误的,因为 _age 是 Animal 类的私有成员,即使 sheep 是 Animal 的公共派生类,它也不能直接访问基类的私有成员。

#include<iostream>  
using namespace std;class Animal
{
public:void work(){cout << "Animal is working" << endl;}
protected:void sleep(){cout << "Animal is sleeping" << endl;}
private:int _age;int _sex;
};class sheep : public Animal
{
public:sheep() {work();}void wakeUpAndWork() {sleep();work();}
};class dog : protected Animal
{
public:dog(){work();sleep();}// 如果需要,可以在这里添加一个函数来间接调用sleep(),但这在main中仍然不可见  // void dogSleep() { sleep(); }  
};class cat : private Animal
{
public:cat(){work();sleep();}// 注意:由于private继承,这里不能添加任何公开的成员函数来调用sleep()或work(),  // 除非它们有其他的用途(比如设置或获取私有数据)  
};int main()
{sheep mySheep;//mySheep.sleep();   // 这行是错误的,因为sleep()在基类中是protected权限,因此在sheep类外部不可见  //mySheep._age = 10; // 错误:_age在基类中是私有的,不能直接访问  mySheep.wakeUpAndWork();dog myDog;//myDog._age = 10; // 错误:_age在基类中是私有的,不能直接访问  // myDog.sleep(); // 这行是错误的,因为sleep()在基类中是protected权限,因此在dog类外部不可见  cat myCat;//myCat._age = 10; // 错误:_age在基类中是私有的,不能直接访问  //myCat.sleep();   // 这行是错误的,因为sleep()在基类中是protected权限,因此在cat类外部不可见  return 0;
}

子类所占字节大小

如下,代码运行之后会输出的字节大小是多少:

class Animal
{
public:void work(){cout << "Animal is working" << endl;}
protected:void sleep(){cout << "Animal is sleeping" << endl;}
private:int _age;int _sex;
};class sheep : public Animal
{
public:
private:int color;
};
int main()
{cout << sizeof(sheep) << endl;return 0;
}

首先打开VS开发人员命令提示符(Developer Command Prompt ..),然后输入当前目录所在的盘符.

然后输出下面命令:

cd 当前目录绝对路径
dir

 就会有下面的显示:

然后我们输入下面命令:

cl /d1 reportSingleClassLayout要查的类名 "filename.cpp"

就会弹出下面的布局:

我们可以观察到子类sheep的字节大小为12,其中有2个继承与父类Animal的成员变量占了8字节,然后自己的一个成员变量占了4字节.

继承的构造析构顺序

如下代码:

#include<iostream>  
using namespace std;class Animal
{
public:Animal(){cout << "父类构造函数" << endl;}~Animal(){cout << "父类析构函数" << endl;}    void work(){cout << "Animal is working" << endl;}
protected:void sleep(){cout << "Animal is sleeping" << endl;}
private:int _age;int _sex;
};
class sheep : public Animal
{
public:sheep(){cout << "子类构造函数" << endl;}~sheep(){cout << "子类析构函数" << endl;}
};int main()
{sheep s;return 0;
}

通过运行结果,我们可以总结一条规则:

继承先调父类构造,再调子类构造.析构刚好相反.

多继承 

多继承就是一个子类继承多个父类.

如下所示,sheep类同时继承Animal,Pet,Mammals三个几类.同时,三个基类 都有共同的成员变量_age:


//动物类
class Animal
{
public:   void work(){cout << "Animal is working" << endl;}
protected:void sleep(){cout << "Animal is sleeping" << endl;}int _age;int _sex;
};//宠物类
class Pet
{
public:int _age;
};//哺乳动物类
class Mammals
{
public:int _age;
};
class sheep : public Animal,public Pet,public Mammals
{
};

 那我子类对象想调用_age就要通过指定作用域的方式去指定调用哪个父类下的_age:

int main()
{sheep s;s.Pet::_age = 10;return 0;
}

 

同名成员的处理

如下所示,子类sheep和父类Animal都有成员变量_age,其中父类的_age值为10,子类的_age值为20.此时通过子类对象调用_age,会输出10还是20呢?


//动物类
class Animal
{
public:   Animal(){_age = 10;}void work(){cout << "Animal is working" << endl;}void sleep(){cout << "Animal is sleeping" << endl;}int _age;int _sex;
};class sheep : public Animal
{
public:sheep(){_age = 20;}int _age;
};int main()
{sheep s;cout << s._age << endl;return 0;
}

答案是20:

规则如下:

int main()
{sheep s;cout << s.Animal::_age << endl;return 0;
}

同名静态成员的处理

首先要知道什么是静态成员.所谓的静态成员有三个特点:

静态成员变量的特性:

1.所有的静态对象共享一份数据

2.类内声明,类外定义

3.编译阶段分配内存

静态成员函数的特性:

1.只能访问静态的成员变量,不能访问非静态的成员变量

2.所有的对象共享同一份函数实例

静态成员调用规则

 静态同名成语的调用规则和非静态同名成员的调用规则一样:

 有如下两个类:

class Base
{
public:static int _a;
};
int Base::_a = 100;
class Son: public Base
{
public:static int _a;
};
int Son::_a=200;

 我们如果不指定作用域就默认调用子类:

int main()
{Son s;cout << s._a << endl;return  0;
}

指定了父类才会调父类:

int main()
{Son s;cout << s.Base::_a << endl;return  0;
}int main()
{Son s;cout << s.Base::_a << endl;return  0;
}

但是静态成员调用和非静态成员有一点区别,静态成员调用有2种方式:

1.通过对象调用

2.通过类名调用

通过类名的方式调同名静态变量

调子类的_age:

int main()
{Son s;cout << Son::_a << endl;return  0;
}

调父类的_age:

int main()
{Son s;cout << Base::_a << endl;return  0;
}

如果我想通过子类去调父类的_age呢:

int main()
{Son s;cout <<Son::Base::_a << endl;return  0;
}

调用同名静态成员函数

class Base
{
public:static void test();static int _a;
};
int Base::_a = 100;
void Base::test()
{cout << "1" << endl;
}class Son: public Base
{
public:static void test();static int _a;
};
int Son::_a=200;
void Son::test()
{cout << "2" << endl;
}

 还是和上面一样,没限定符默认调用子类的,有限定符指定父类调父类的:

int main()
{Son s;s.test();return  0;
}

int main()
{Son s;s.Base::test();return  0;
}

通过类名的方式调用同名静态成员函数

int main()
{Son s;Son::test();return  0;
}

int main()
{Son s;Base::test();return  0;
}

菱形继承的案列


http://www.mrgr.cn/news/8778.html

相关文章:

  • Chrome 渲染器中的对象转换到 RCE
  • Springboot 定时任务cron表达式
  • GoWeb 设置别名和多环境配置
  • 动手学深度学习(pytorch)学习记录15-正则化、权重衰减[学习记录]
  • Flat Ads:全球金融应用现状与发展趋势深度解析
  • RocketMQ 与 Spring Cloud Stream之事务消息配置
  • 【Vue】计算属性和监听属性
  • springdatajpa解决postgresql数据库字段驼峰命名问题
  • C++系列-多态的基本语法
  • repo的patch转换成git am能打的patch
  • 数据结构:(OJ题力扣 20). 有效的括号
  • 怎样写好提示词(Prompt) 一
  • CyberScraper-2077+simple-one-api:使用大模型爬虫
  • Xv6驱动(一):PLIC
  • 51单片机——数码管控制
  • linux驱动:(16)在设备树添加自定义节点
  • 23次8.7(mysql主从脚本与mysql详细语句介绍)
  • Linux 终端显示 Git 当前所在分支
  • RabbitMQ安装 docker
  • 【Redis】Redis 持久化 -- RDB AOF