C++学习-继承中的作用域(10)
2012-04-15 15:18
363 查看
作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.csdn.net/gzshun
在继承过程中,从基类派生出派生类,可能出现重名的成员,包括数据成员或成员函数,这些属于作用域方面的内容。
一、基类的引用或指针访问派生类
基类的引用或指针只能访问派生类中属于基类的部分成员,不能访问派生类的部分,否则就会编译出错。
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void BasePrint() const
{
cout << "调用基类的成员函数" << endl;
}
};
class CDerived : public CBase
{
public:
void DerivedPrint() const
{
cout << "调用派生类的成员函数" << endl;
}
};
int main()
{
CDerived obj;
CBase *bObj = &obj;
CDerived * dObj = &obj;
bObj->BasePrint();
// bObj->DerivedPrint();//错误,基类指针不能调用派生类的部分
dObj->BasePrint();
dObj->DerivedPrint();
return 0;
}
执行结果:
调用基类的成员函数
调用基类的成员函数
调用派生类的成员函数
二、重复的数据成员
有时基类和派生类有同名的数据成员,甚至可能在基类和间接基类中都有同名的数据成员。
这种情况一般会出现在:从 由另一个程序员创建的基类中派生自己的类,可能不知道在基类中有什么私有数据成员,此时自己又创建了与基类当中同名的数据成员。
当然这种情况是不会阻碍继承的,编译器做了很多的工作。但这里主要是介绍如何从派生类中调用派生类的同名私有成员或者基类的私有成员。
例子:
CBase是CDerived的父类,它们拥有一个同名成员为mVal。
在派生类中
1.默认是访问自己的数据成员mVal;
2.若要访问基类的mVal,要用这种格式"<类名>::<数据成员>", CBase::mVal
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
CBase(int x = 120) : mVal(x) {}
int mVal;
};
class CDerived : public CBase
{
public:
CDerived(int x = 100) : mVal(x) {}
void PrintValue() const
{
cout << "犀利爹的年龄:" << CBase::mVal << endl
<< "犀利哥的年龄:" << mVal << endl;
}
int mVal;
};
int main()
{
CDerived human;
human.PrintValue();
return 0;
}
执行结果:
犀利爹的年龄:120
犀利哥的年龄:100
三、重复的成员函数
基类与派生类若存在重复的成员函数,则有2种情况:
第一种情况:函数名相同,参数列表不同,如果将基类的成员函数的作用域引入派生类中,就是重载版本了
第二种情况:函数的所有方面都相同,包括函数名,参数列表与返回值
第一种情况:
函数名相同,参数列表不同,这跟函数的重载一样。但这并不是函数的重载,因为函数的重载必须在同一个作用域中定义,而基类和派生类定义了不同的作用域。在这里,可以使用using在派生类中声明基类的函数,将基类的函数的作用域引入派生类中的作用域,让这里的函数成为重载版本,来个小例子。
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void Print(const char *str)
{
cout << "调用基类的打印函数: str = " << str << endl;
}
};
class CDerived : public CBase
{
public:
using CBase::Print;
void Print(int val)
{
cout << "调用派生类的打印函数: val = " << val << endl;
}
};
int main()
{
CDerived obj;
obj.Print(2);
obj.Print("hello");
return 0;
}
执行结果:
调用派生类的打印函数: val = 2
调用基类的打印函数: str = hello
这里如果没有"using CBase::Print;"的声明,那么将会编译错误。
第二种情况:
函数的所有方面都相同,包括函数名,参数列表与返回值,在派生类中,要调用基类的同名成员函数,方法跟同名数据成员的方法一样,格式是:"<类名>::<成员函数>"。
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void Print()
{
cout << "调用基类的成员函数" << endl;
}
};
class CDerived : public CBase
{
public:
void Print()
{
cout << "调用派生类的成员函数" << endl;
}
};
int main()
{
CDerived human;
human.Print();
human.CBase::Print();
return 0;
}
执行结果:
调用派生类的成员函数
调用基类的成员函数
四、虚函数的作用域
关于虚函数的内容,就会涉及到动态绑定的知识了,这里先不介绍,主要还是说明类继承的作用域的问题。在类的继承过程中,基类中的成员函数是不是虚函数,将会起着非常大的作用,先来看2个虚函数的例子。
1.在基类中没有使用virtual关键字
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void Print() const
{
cout << "调用CBase::Print()" << endl;
}
};
class CDerived1 : public CBase
{
public:
void Print() const
{
cout << "调用CDerived1::Print()" << endl;
}
};
class CDerived2 : public CDerived1
{
public:
void Print() const
{
cout << "调用CDerived2::Print()" << endl;
}
};
int main()
{
CBase bobj;
CDerived1 d1obj;
CDerived2 d2obj;
CBase *bp1 = &bobj;
CBase *bp2 = &d1obj;
CBase *bp3 = &d2obj;
bp1->Print();
bp2->Print();
bp3->Print();
return 0;
}
执行结果:
调用CBase::Print()
调用CBase::Print()
调用CBase::Print()
2.在基类中使用virtual关键字
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
virtual void Print() const
{
cout << "调用CBase::Print()" << endl;
}
};
class CDerived1 : public CBase
{
public:
void Print() const
{
cout << "调用CDerived1::Print()" << endl;
}
};
class CDerived2 : public CDerived1
{
public:
void Print() const
{
cout << "调用CDerived2::Print()" << endl;
}
};
int main()
{
CBase bobj;
CDerived1 d1obj;
CDerived2 d2obj;
CBase *bp1 = &bobj;
CBase *bp2 = &d1obj;
CBase *bp3 = &d2obj;
bp1->Print();
bp2->Print();
bp3->Print();
return 0;
}
执行结果:
调用CBase::Print()
调用CDerived1::Print()
调用CDerived2::Print()
1,2两个例子可以看到,一个virtual的关键字起着这么大的作用。当基类的成员函数使用virtual关键字修饰的话,基类指针会根据指向的对象的实际类型来寻找相应的类的成员函数的定义。要获得动态绑定,必须通过基类的引用或指针调用虚成员。
在第二个例子中,将Print函数声明为虚函数,这样子编译器会生成代码,在运行时基于引用或指针所绑定的对象的实际类型进行调用。bp2指向CDerived1对象,bp3指向CDerived2对象,所以都是调用属于自己的Print函数版本。
来源:http://blog.csdn.net/gzshun
在继承过程中,从基类派生出派生类,可能出现重名的成员,包括数据成员或成员函数,这些属于作用域方面的内容。
一、基类的引用或指针访问派生类
基类的引用或指针只能访问派生类中属于基类的部分成员,不能访问派生类的部分,否则就会编译出错。
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void BasePrint() const
{
cout << "调用基类的成员函数" << endl;
}
};
class CDerived : public CBase
{
public:
void DerivedPrint() const
{
cout << "调用派生类的成员函数" << endl;
}
};
int main()
{
CDerived obj;
CBase *bObj = &obj;
CDerived * dObj = &obj;
bObj->BasePrint();
// bObj->DerivedPrint();//错误,基类指针不能调用派生类的部分
dObj->BasePrint();
dObj->DerivedPrint();
return 0;
}
执行结果:
调用基类的成员函数
调用基类的成员函数
调用派生类的成员函数
二、重复的数据成员
有时基类和派生类有同名的数据成员,甚至可能在基类和间接基类中都有同名的数据成员。
这种情况一般会出现在:从 由另一个程序员创建的基类中派生自己的类,可能不知道在基类中有什么私有数据成员,此时自己又创建了与基类当中同名的数据成员。
当然这种情况是不会阻碍继承的,编译器做了很多的工作。但这里主要是介绍如何从派生类中调用派生类的同名私有成员或者基类的私有成员。
例子:
CBase是CDerived的父类,它们拥有一个同名成员为mVal。
在派生类中
1.默认是访问自己的数据成员mVal;
2.若要访问基类的mVal,要用这种格式"<类名>::<数据成员>", CBase::mVal
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
CBase(int x = 120) : mVal(x) {}
int mVal;
};
class CDerived : public CBase
{
public:
CDerived(int x = 100) : mVal(x) {}
void PrintValue() const
{
cout << "犀利爹的年龄:" << CBase::mVal << endl
<< "犀利哥的年龄:" << mVal << endl;
}
int mVal;
};
int main()
{
CDerived human;
human.PrintValue();
return 0;
}
执行结果:
犀利爹的年龄:120
犀利哥的年龄:100
三、重复的成员函数
基类与派生类若存在重复的成员函数,则有2种情况:
第一种情况:函数名相同,参数列表不同,如果将基类的成员函数的作用域引入派生类中,就是重载版本了
第二种情况:函数的所有方面都相同,包括函数名,参数列表与返回值
第一种情况:
函数名相同,参数列表不同,这跟函数的重载一样。但这并不是函数的重载,因为函数的重载必须在同一个作用域中定义,而基类和派生类定义了不同的作用域。在这里,可以使用using在派生类中声明基类的函数,将基类的函数的作用域引入派生类中的作用域,让这里的函数成为重载版本,来个小例子。
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void Print(const char *str)
{
cout << "调用基类的打印函数: str = " << str << endl;
}
};
class CDerived : public CBase
{
public:
using CBase::Print;
void Print(int val)
{
cout << "调用派生类的打印函数: val = " << val << endl;
}
};
int main()
{
CDerived obj;
obj.Print(2);
obj.Print("hello");
return 0;
}
执行结果:
调用派生类的打印函数: val = 2
调用基类的打印函数: str = hello
这里如果没有"using CBase::Print;"的声明,那么将会编译错误。
第二种情况:
函数的所有方面都相同,包括函数名,参数列表与返回值,在派生类中,要调用基类的同名成员函数,方法跟同名数据成员的方法一样,格式是:"<类名>::<成员函数>"。
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void Print()
{
cout << "调用基类的成员函数" << endl;
}
};
class CDerived : public CBase
{
public:
void Print()
{
cout << "调用派生类的成员函数" << endl;
}
};
int main()
{
CDerived human;
human.Print();
human.CBase::Print();
return 0;
}
执行结果:
调用派生类的成员函数
调用基类的成员函数
四、虚函数的作用域
关于虚函数的内容,就会涉及到动态绑定的知识了,这里先不介绍,主要还是说明类继承的作用域的问题。在类的继承过程中,基类中的成员函数是不是虚函数,将会起着非常大的作用,先来看2个虚函数的例子。
1.在基类中没有使用virtual关键字
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
void Print() const
{
cout << "调用CBase::Print()" << endl;
}
};
class CDerived1 : public CBase
{
public:
void Print() const
{
cout << "调用CDerived1::Print()" << endl;
}
};
class CDerived2 : public CDerived1
{
public:
void Print() const
{
cout << "调用CDerived2::Print()" << endl;
}
};
int main()
{
CBase bobj;
CDerived1 d1obj;
CDerived2 d2obj;
CBase *bp1 = &bobj;
CBase *bp2 = &d1obj;
CBase *bp3 = &d2obj;
bp1->Print();
bp2->Print();
bp3->Print();
return 0;
}
执行结果:
调用CBase::Print()
调用CBase::Print()
调用CBase::Print()
2.在基类中使用virtual关键字
[cpp] view plaincopy
#include <iostream>
using namespace std;
class CBase
{
public:
virtual void Print() const
{
cout << "调用CBase::Print()" << endl;
}
};
class CDerived1 : public CBase
{
public:
void Print() const
{
cout << "调用CDerived1::Print()" << endl;
}
};
class CDerived2 : public CDerived1
{
public:
void Print() const
{
cout << "调用CDerived2::Print()" << endl;
}
};
int main()
{
CBase bobj;
CDerived1 d1obj;
CDerived2 d2obj;
CBase *bp1 = &bobj;
CBase *bp2 = &d1obj;
CBase *bp3 = &d2obj;
bp1->Print();
bp2->Print();
bp3->Print();
return 0;
}
执行结果:
调用CBase::Print()
调用CDerived1::Print()
调用CDerived2::Print()
1,2两个例子可以看到,一个virtual的关键字起着这么大的作用。当基类的成员函数使用virtual关键字修饰的话,基类指针会根据指向的对象的实际类型来寻找相应的类的成员函数的定义。要获得动态绑定,必须通过基类的引用或指针调用虚成员。
在第二个例子中,将Print函数声明为虚函数,这样子编译器会生成代码,在运行时基于引用或指针所绑定的对象的实际类型进行调用。bp2指向CDerived1对象,bp3指向CDerived2对象,所以都是调用属于自己的Print函数版本。
相关文章推荐
- C++学习-继承中的作用域(10)
- c++基础10:继承和派生 虚函数的作用 多态性概念 纯虚函数和抽象类的概念
- C++学习笔记(10)——虚基类的作用
- c++基础学习10-c++的继承
- C++ 学习之路(10):继承与派生
- C++基础学习10:继承
- C++ FAQ学习笔记 23章 继承 — 你所不知道的
- C++学习笔记(7)——多基类继承的构造函数的调用
- c++学习-继承
- C++入门学习:多继承的概念及其二义性、虚继承的概念
- SSH与SSM学习之SSH整合10——扩大session作用范围
- c++中子类的继承和调用父类构造函数的方法——学习c++笔记
- c++学习笔记 继承运用
- 【学习C++】C++ Primer Plus (第六版)第七章编程练习6-10
- 深入浅出Windows 10 通用应用开发学习笔记(C++) 3-1
- C++学习笔记——继承与派生
- JAVA学习笔记10 - 继承
- C++入门学习笔记(四)--继承与多态
- [C++学习笔记]继承
- C++学习-多继承和虚基类(11) http://blog.csdn.net/gzshun/article/details/7300458