继承中的几种关系
1. is a的关系
比如说基类狗,派生了肉狗和宠物狗,我们就可以说肉狗是狗,也可以说宠物狗是狗,这就是is a的关系。但是需要注意的是,并非所有的继承都是is a的关系。
2. has a的关系
继承中的权限继承
class Base
{
public:
int public_i_;
protected:
int protected_i_;
private:
int private_i_;
};
class A :
public Base //
public继承所有的权限都没有发生改变
{
void SetI()
{
protected_i_ =
100;
}
};
class B :
protected Base //
protected 继承会将基类中
public的权限改为
protected权限
{
void SetI()
{
protected_i_ =
100;
}
};
class C :
private Base // 将基类中的
protected和
public权限都改为
private权限
{
void SetI()
{
protected_i_ =
100;
}
};
基类中有默认构造函数的继承中的构造和析构顺序
#include <iostream>
class Base
{
public:
Base()
{
std::
cout <<
"Base::Base()" <<
std::endl;
}
~Base()
{
std::
cout <<
"Base::~Base()" <<
std::endl;
}
int GetNum()
{
return num_;
}
private:
int num_;
};
class A :
public Base
{
public:
A()
{
std::
cout <<
"A::A()" <<
std::endl;
}
~A()
{
std::
cout <<
"A::~A()" <<
std::endl;
}
};
int main()
{
A demo;
demo.GetNum();
return 0;
}
基类中没有默认构造函数的继承中的构造和析构顺序
#include <iostream>
class Base
{
public:
Base(
int num) : num_(num)
{
std::
cout <<
"Base::Base()" <<
std::endl;
}
~Base()
{
std::
cout <<
"Base::~Base()" <<
std::endl;
}
int GetNum()
{
return num_;
}
private:
int num_;
};
class A :
public Base
{
public:
A() : Base(
10)
{
std::
cout <<
"A::A()" <<
std::endl;
}
~A()
{
std::
cout <<
"A::~A()" <<
std::endl;
}
};
int main()
{
A demo;
demo.GetNum();
return 0;
}
继承中的函数,派生类中没有实现基类的函数方法
#include <iostream>
class Base
{
public:
Base(
int num) : num_(num)
{
std::
cout <<
"Base::Base()" <<
std::endl;
}
~Base()
{
std::
cout <<
"Base::~Base()" <<
std::endl;
}
int GetNum()
const
{
return num_;
}
private:
int num_;
};
class A :
public Base
{
public:
A(
int num) : Base(
0), num_(num)
{
std::
cout <<
"A::A()" <<
std::endl;
}
~A()
{
std::
cout <<
"A::~A()" <<
std::endl;
}
private:
int num_;
};
int main()
{
A demo(
100);
demo.GetNum();
std::
cout << demo.GetNum() <<
std::endl;
return 0;
}
结果是
另一种情况是,派生类中实现了基类的函数方法
#include <iostream>
class Base
{
public:
Base(
int num) : num_(num)
{
std::
cout <<
"Base::Base()" <<
std::endl;
}
~Base()
{
std::
cout <<
"Base::~Base()" <<
std::endl;
}
int GetNum()
const
{
return num_;
}
private:
int num_;
};
class A :
public Base
{
public:
A(
int num) : Base(
0), num_(num)
{
std::
cout <<
"A::A()" <<
std::endl;
}
~A()
{
std::
cout <<
"A::~A()" <<
std::endl;
}
int GetNum()
const
{
return num_;
}
private:
int num_;
};
int main()
{
A demo(
100);
demo.GetNum();
std::
cout << demo.GetNum() <<
std::endl;
return 0;
}
输出结果是
基类指针指向派生类对象的函数调用情况
#include <iostream>
class Base
{
public:
Base(
int num) : num_(num)
{
std::
cout <<
"Base::Base()" <<
std::endl;
}
~Base()
{
std::
cout <<
"Base::~Base()" <<
std::endl;
}
int GetNum()
const
{
std::
cout <<
"Base::GetNum()" <<
std::endl;
return num_;
}
private:
int num_;
};
class A :
public Base
{
public:
A(
int num) : Base(
0), num_(num)
{
std::
cout <<
"A::A()" <<
std::endl;
}
~A()
{
std::
cout <<
"A::~A()" <<
std::endl;
}
int GetNum()
const
{
std::
cout <<
"A::GetNum()" <<
std::endl;
return num_;
}
private:
int num_;
};
int main()
{
Base base(
100);
A a(
200);
Base *pBase = &a;
std::
cout << pBase->GetNum() <<
std::endl;
return 0;
}
输出结果是
使用虚函数在继承中函数调用的好处
#include <iostream>
class Base
{
public:
Base(
int num) : num_(num)
{
std::
cout <<
"Base::Base()" <<
std::endl;
}
~Base()
{
std::
cout <<
"Base::~Base()" <<
std::endl;
}
virtual int GetNum()
const
{
std::
cout <<
"Base::GetNum()" <<
std::endl;
return num_;
}
private:
int num_;
};
class A :
public Base
{
public:
A(
int num) : Base(
0), num_(num)
{
std::
cout <<
"A::A()" <<
std::endl;
}
~A()
{
std::
cout <<
"A::~A()" <<
std::endl;
}
int GetNum()
const
{
std::
cout <<
"A::GetNum()" <<
std::endl;
return num_;
}
private:
int num_;
};
int main()
{
Base base(
100);
A a(
200);
Base *pBase = &a;
std::
cout << pBase->GetNum() <<
std::endl;
return 0;
}
结果是 为什么会这样呢?因为使用了虚函数,虚函数用来指明派生类中需要调用的函数,也就是说会在派生类中优先寻找要调用的函数,而不是在基类中寻找这个函数。
虚函数的其它例子
派生类继承并操作了基类中的成员变量
继承了基类中的str_变量,并对其进行了操作,导致内存泄漏,并且在析构的时候重复析构。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class String
{
public:
String(
const char *str =
"")
{
unsigned int len =
strlen(str);
str_ =
new char[len +
sizeof(
char)];
strcpy(str_, str);
}
~String()
{
delete[] str_;
}
String &
operator+=(
const String &other)
{
unsigned int len =
strlen(str_) +
strlen(other.str_);
char *temp =
new char[len +
sizeof(
char)];
strcpy(temp, str_);
strcat(temp, other.str_);
delete[] str_;
str_ = temp;
return *
this;
}
String
operator+(
const String &other)
{
String demo;
demo += other;
return demo;
}
protected:
char *str_;
};
class MyString :
public String
{
public:
MyString(
const char *str =
"PoEdu")
{
unsigned int len =
strlen(str);
str_ =
new char[len +
sizeof(
char)];
strcpy(str_, str);
}
MyString
operator+(
const MyString &other)
{
}
~MyString()
{
delete[] str_;
str_ =
nullptr;
}
};
int main()
{
MyString str;
return 0;
}
调用基类的构造函数来操作基类的成员变量,而不是在派生类中直接操作基类只能怪的成员变量
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class String
{
public:
String(
const char *str =
"")
{
unsigned int len =
strlen(str);
str_ =
new char[len +
sizeof(
char)];
strcpy(str_, str);
}
~String()
{
delete[] str_;
}
String &
operator+=(
const String &other)
{
unsigned int len =
strlen(str_) +
strlen(other.str_);
char *temp =
new char[len +
sizeof(
char)];
strcpy(temp, str_);
strcat(temp, other.str_);
delete[] str_;
str_ = temp;
return *
this;
}
String
operator+(
const String &other)
{
String demo;
demo += other;
return demo;
}
protected:
char *str_;
};
class MyString :
public String
{
public:
MyString(
const char *str =
"PoEdu") : String(str)
{
}
MyString
operator+(
const MyString &other)
{
}
~MyString()
{
delete[] str_;
str_ =
nullptr;
}
};
int main()
{
MyString str;
return 0;
}
这样虽然保证了只对基类中的成员变量操作一次,但是在析构的时候还是会重复析构。
虚析构函数的使用情形
没有虚析构函数的析构情形
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class A
{
public:
~A()
{
std::
cout <<
"~A()" <<
std::endl;
}
};
class B :
public A
{
public:
~B()
{
std::
cout <<
"~B(()" <<
std::endl;
}
};
int main()
{
A *pA =
new B();
delete pA;
return 0;
}
运行结果如下图所示 这样只是调用了基类中的析构函数,并没有调用派生类中的析构函数
有虚析构函数的析构情形
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class A
{
public:
virtual ~A()
{
std::
cout <<
"~A()" <<
std::endl;
}
};
class B :
public A
{
public:
~B()
{
std::
cout <<
"~B(()" <<
std::endl;
}
};
int main()
{
A *pA =
new B();
delete pA;
return 0;
}
运行结果如下: 这样就会把派生类中的析构函数也调用了。
正确的做法是只需要管理好本类中的成员变量即可,无需管理继承下来的成员变量(其实我觉得在实际的应用中,成员变量应该都是private的,是不需要被子类继承的,只有函数方法才需要被继承下来)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class String
{
public:
String(
const char *str =
"")
{
unsigned int len =
strlen(str);
str_ =
new char[len +
sizeof(
char)];
strcpy(str_, str);
}
~String()
{
delete[] str_;
}
String &
operator+=(
const String &other)
{
unsigned int len =
strlen(str_) +
strlen(other.str_);
char *temp =
new char[len +
sizeof(
char)];
strcpy(temp, str_);
strcat(temp, other.str_);
delete[] str_;
str_ = temp;
return *
this;
}
String
operator+(
const String &other)
{
String demo;
demo += other;
return demo;
}
protected:
char *str_;
};
class MyString :
public String
{
public:
MyString(
const char *str =
"PoEdu") : String(str)
{
length_ =
new int(
0);
}
MyString
operator+(
const MyString &other)
{
}
~MyString()
{
delete length_;
}
private:
int *length_;
};
int main()
{
return 0;
}
这样就保证了程序的正确运行。
虚函数的调用
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class String
{
public:
String(
const char *str =
"")
{
unsigned int len =
strlen(str);
str_ =
new char[len +
sizeof(
char)];
strcpy(str_, str);
}
String(
const String &other)
{
unsigned int len =
strlen(other.str_);
str_ =
new char[len +
sizeof(
char)];
strcpy(str_, other.str_);
}
~String()
{
delete[] str_;
}
String &
operator+=(
const String &other)
{
unsigned int len =
strlen(str_) +
strlen(other.str_);
char *temp =
new char[len +
sizeof(
char)];
strcpy(temp, str_);
strcat(temp, other.str_);
delete[] str_;
str_ = temp;
return *
this;
}
String
operator+(
const String &other)
{
String demo(*
this);
demo += other;
return demo;
}
friend std::ostream &
operator<<(
std::ostream &os,
const String &other)
{
os << other.str_;
return os;
}
protected:
char *str_;
};
class MyString :
public String
{
public:
MyString(
const char *str =
"PoEdu") : String(str)
{
}
MyString
operator+(
const MyString &other)
{
MyString temp(*
this);
temp += other;
temp +=
"---------PoEdu";
return temp;
}
};
int main()
{
MyString str(
"I Love ");
String *pString = &str;
std::
cout << str + (
"Mark") <<
std::endl;
std::
cout << *pString + (
"Mark") <<
std::endl;
return 0;
}
运行结果
继承中的虚析构函数
基类中的析构函数不是虚析构函数,而是普通函数的情况:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class A
{
public:
~A()
{
std::
cout <<
"~A()" <<
std::endl;
}
};
class B :
public A
{
public:
B() : A()
{
demo_ =
new int(
0);
}
~B()
{
std::
cout <<
"~B()" <<
std::endl;
delete demo_;
}
private:
int *demo_;
};
int main()
{
A * pA =
new B();
delete pA;
return 0;
}
运行结果是: 从结果上可以看出,在delete的时候,只是调用了基类中的析构函数,并没有调用派生类中的析构函数,那么,如何能让派生类中的析构函数也被调用呢?请看下面的情况!!! 基类中的析构函数是虚析构函数的情况:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class A
{
public:
virtual ~A()
{
std::
cout <<
"~A()" <<
std::endl;
}
};
class B :
public A
{
public:
B() : A()
{
demo_ =
new int(
0);
}
~B()
{
std::
cout <<
"~B()" <<
std::endl;
delete demo_;
}
private:
int *demo_;
};
int main()
{
A * pA =
new B();
delete pA;
return 0;
}
运行结果: 关于继承的构造和析构顺序的总结: 一般的继承体系的类 基类构造 基类成员构造 子类构造 子类析构 基类成员析构 基类析构
继承中的虚继承
著名的菱形继承
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class A
{
public:
int a_;
};
class B :
public A
{
public:
int b_;
};
class C :
public A
{
public:
int c_;
};
class D :
public B,
public C
{
};
int main()
{
D d;
d.a_;
return 0;
}
此时在编译的时候就会出错,如下所示: 但是,我们也是可以访问的
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class A
{
public:
int a_;
};
class B :
public A
{
public:
int b_;
};
class C :
public A
{
public:
int c_;
};
class D :
public B,
public C
{
};
int main()
{
D d;
d.B::a_;
d.C::a_;
return 0;
}
指明要访问哪个基类中的变量,就不会出现编译错误了。 这样虽然解决了编译错误的问题,但是我们不需要这样的情况,我们只需要一份变量就可以了,那么这样该怎么解决呢?我们可以通过虚继承来解决这个问题!!!
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class A
{
public:
int a_;
};
class B :
virtual public A
{
public:
int b_;
};
class C :
virtual public A
{
public:
int c_;
};
class D :
virtual public B,
virtual public C
{
};
int main()
{
D d;
d.a_;
return 0;
}
此时编译也就可以通过了。
继承中的纯虚函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class Animal
{
public:
virtual void Cry() =
0;
};
class Dog :
public Animal
{
public:
};
int main()
{
Dog dog;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class Animal
{
public:
virtual void Cry() =
0;
};
class Dog :
public Animal
{
public:
void Cry()
{
}
};
int main()
{
Dog dog;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class Animal
{
public:
virtual void Cry() =
0;
};
class Dog :
public Animal
{
public:
void Cry()
{
}
};
class A :
public Dog
{
};
int main()
{
Dog dog;
A a;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
class Animal
{
public:
virtual void Cry() =
0;
virtual void Jump() =
0;
};
class Dog :
public Animal
{
public:
void Cry()
{
}
};
class A :
public Dog
{
};
int main()
{
Dog *dog =
new A();
dog->Cry();
return 0;
}
转载请注明原文地址: https://ju.6miu.com/read-662731.html