加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

【C++学习笔记】父类指针指向子类对象

发布时间:2020-12-15 04:49:07 所属栏目:百科 来源:网络整理
导读:? ? ? ? 虚函数的作用主要是实现了多态的机制。简而言之就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。但仅仅可调用父类含有的函数,非父类函数不能调用。 普通虚函数调用 假设我们有下面的类层次: #include using names

? ? ? ? 虚函数的作用主要是实现了多态的机制。简而言之就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。但仅仅可调用父类含有的函数,非父类函数不能调用。

普通虚函数调用

假设我们有下面的类层次:

#include


using namespace std;

class A


{


public:


?? ?A(){};


?? ?~A(){}

?? ?virtual void foo()


?? ?{


?? ??? ?cout << "A::foo() is called" << endl;


?? ?}


};


class B :public A


{


public:


?? ?B(){}


?? ?~B(){}

?? ?void foo()


?? ?{


?? ??? ?cout << "B::foo() is called" << endl;


?? ?}


?? ?void fun()


?? ?{


?? ??? ?cout << "B::fun() is called" << endl;


?? ?}

?? ?virtual void fun1()


?? ?{


?? ??? ?cout << "B::fun() is called" << endl;


?? ?}


};

int main(void)


{


?? ?A *a = new B();


?? ?a->foo(); ? ? // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!


?? ?// a->fun(); ?// 这里调用错误,提示:error C2039: “fun”: 不是“A”的成员,?e:debugbasetestmain.cpp(8) : 参见“A”的声明


?? ?// a->fun1(); // 这里调用错误,提示:error C2039: “fun1”: 不是“A”的成员,?e:debugbasetestmain.cpp(8) : 参见“A”的声明

?? ?return 0;


}

以上例子说明,子类的虚函数替换了父类的同名虚函数(参见【C++学习笔记】虚函数实现多态原理)实现通过父类调用子类函数实现的功能。非父类含有的函数无法调用。

构造函数与析构函数调用

假设有如下类层次:

class A


{


public:


?? ?A(){ cout << "A::A() is called" << endl; };


?? ?~A(){ cout << "A::~A() is called" << endl; }

?? ?virtual void foo()


?? ?{


?? ??? ?cout << "A::foo() is called" << endl;


?? ?}


};


class B :public A


{


public:


?? ?B(){ cout << "B::B() is called" << endl; }


?? ?~B(){ cout << "B::~B() is called" << endl; }

?? ?void foo()


?? ?{


?? ??? ?cout << "B::foo() is called" << endl;


?? ?}


};

int main(void)


{


?? ?A *a = new B();


?? ?a->foo(); ? ? // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!

?? ?delete a;

?? ?return 0;


}

程序输出:

A::A() is called

B::B() is called

B::foo() is called

A::~A() is called

由程序输出可以看出,程序先构造父类,再构造子类,析构时由于父类的析构函数非虚函数,所以未调用子类的析构函数。若子类的构造函数存在动态内存分配,则会存在内存泄漏的问题。若父类析构函数为虚函数,则程序输出如下:

A::A() is called

B::B() is called

B::foo() is called

B::~B() is called

A::~A() is called

子类的动态内存分配就可以被释放。

父子类指针强制转换的安全性

假设有如使用:

int main(void)


{


?? ?A *a = new A();

?? ?B *b = static_cast(a);


?? ?b->fun();?

?? ?delete b;

?? ?return 0;


}

程序输出:

A::A() is called

B::foo() is called

A::~A() is called

父类指针强制转换为子类指针,并调用了子类函数,释放时释放的是父类A指针,并未释放B指针。父类指针a强制转换过程中并未调用子类B构造函数,释放时也未调用子类B析构函数。说明父类向下强制转换存在较大风险,如下所示:

class A


{


public:


?? ?A(){ cout << "A::A() is called" << endl; };


?? ?virtual~A(){ cout << "A::~A() is called" << endl; }

?? ?virtual void foo()


?? ?{


?? ??? ?cout << "A::foo() is called" << endl;


?? ?}


};


class B :public A


{


public:


?? ?B()


?? ?{?


?? ??? ?cout << "B::B() is called" << endl;?


?? ??? ?pt = new int[10];


?? ??? ?for (int i = 0; i < 10; i++)


?? ??? ??? ?pt[i] = i;

?? ?}


?? ?virtual~B()


?? ?{?


?? ??? ?cout << "B::~B() is called" << endl;?


?? ??? ?delete [] pt;


?? ?}

?? ?void foo()


?? ?{


?? ??? ?cout << "B::foo() is called" << endl;


?? ?}

?? ?void fun()


?? ?{


?? ??? ?cout << "B::fun() is called" << endl;


?? ??? ?cout << pt[0] << endl;


?? ?}

?? ?int *pt;


};

int main(void)


{


?? ?A *a = new A();

?? ?B *b = static_cast(a);


?? ?b->fun();?

?? ?delete a;

?? ?return 0;


}

程序输出异常,因为子类B调用的函数使用了需要在子类构造函数中动态分配的内存。因为强制转换过程中,未调用子类B的构造函数。

int main(void)


{


?? ?A *a = new B();

?? ?B *b = static_cast(a);


?? ?b->fun();?

?? ?delete a;

?? ?return 0;


}

程序输出:

A::A() is called

B::B() is called

B::fun() is called

B::~B() is called

A::~A() is called

父类指针强制转换为子类指针,并调用了子类函数,释放时释放的是父类A指针,并释放B指针。此情况说明:父类指针a指向子类指针,再将父类指针转换为子类B使用的安全的。可能因为父类指针a指向的内存块包含构造子类B的所有空间。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读