继承的概念
总结:继承的本质就是复用,集成的作用就是减少重复代码
例如有羊,猫,狗三个动物.每个动物都有性别,年龄等属性.都有行走,睡觉等方法.我们可以为每个动物写一个类,如下:
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
的派生类(如 sheep
、dog
、cat
)内部,以及这些派生类的友元函数中被访问。在派生类的外部(如 main
函数中),sleep()
是不可见的,无论是通过 sheep
、dog
还是 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;
}