C++ 多态 https://blog.csdn.net/qq_41605114/article/details/104282305
C++ 继承 https://blog.csdn.net/qq_41605114/article/details/104244620
以上为基础知识预览:
目录
0继承中的同名处理
公有继承:
父类的public和protected原封不动,父类的private无法访问
保护继承:
父类的public和protected都会变成protected,父类的private无法访问
私有继承:
父类的public和protected都会变成private,父类的private无法访问
父类private永远无法访问,public和protected都是可以访问的
但是友元却可以做到!
类A的友元函数(全局)&&友元类可以访问A的private变量
(所以友元不是访问控制属性)
在选择继承方式的时候要注意
继承中出现同名的成员函数,编译器将如何处理?
此处分为两个部分,一般情况和多态情况
1一般情况
子类会将父类中的同名成员(包括成员函数和成员变量)隐藏,如果不主动声明作用域,会自动调用子类中的成员
静态成员(包括成员函数和成员变量)也是一样的,此处就不多做赘述
下面我们来看看实际情况:
1.1含有相同的成员变量
类声明:
class Aclass
{
public:
Aclass();
int sameItem;
};
class Bclass : public Aclass
{
public:
Bclass(const int a);
int sameItem;
};
类函数定义:
Aclass::Aclass()
{
qDebug()<<"进入Aclass构造函数";
this->sameItem = 10;//父类构造,直接默认变量为10
}
Bclass::Bclass(const int a)
{
qDebug()<<"进入Bclass构造函数";
this->sameItem = a;//子类构造,需要赋值操作
}
具体操作:
ui->setupUi(this);
Bclass B(1);
qDebug()<<"B.sameItem:"<<B.sameIte
子类创建对象时,先调用父类的构造函数,然后调用自身构造函数(如果自身没有用户提供的构造函数,系统提供,但是还是会先调用父类构造函数)
如果父类构造函数有参,需要在子类构造中加入赋值操作,所以下面的输出先是完成父类的构造函数。
输出:
显然,输出的子类中的同名变量,隐藏了父类中的同名变量
那么如果想查看父类中的同名变量呢?加上作用域限定符即可
Bclass B(1);
qDebug()<<"B.sameItem:"<<B.sameItem;
qDebug()<<"A.sameItem:"<<B.Aclass::sameItem;
1.2含有相同的成员函数
类声明:
class Aclass
{
public:
Aclass();
int sameItem;
void SameFunction();
};
class Bclass : public Aclass
{
public:
Bclass(const int a);
int sameItem;
void SameFunction();
};
类函数定义:
Aclass::Aclass()
{
qDebug()<<"进入Aclass构造函数";
this->sameItem = 10;//父类构造,直接默认变量为10
}
Bclass::Bclass(const int a)
{
qDebug()<<"进入Bclass构造函数";
this->sameItem = a;//子类构造,需要赋值操作
}
void Aclass::SameFunction()
{
qDebug()<<"进入Aclass::SameFunction构造函数";
}
void Bclass::SameFunction()
{
qDebug()<<"进入Bclass::SameFunction构造函数";
}
具体操作:
Bclass B(1);
B.SameFunction();
可以看到,调用的是子类的同名成员函数,隐藏(hide)了父类的同名成员函数
隐藏(hide):
即:派生类中函数隐藏(屏蔽)了基类中的同名函数。
情形1: 函数名相同、 函数参数相同、 分别位于派生类和基类中、virtual -- 为 覆盖(override);
情形2: 函数名相同、 函数参数相同、 分别位于派生类和基类中 -- 为 隐藏;(即跟覆盖的区别是基类中函数是否为虚函数)
情形3 : 函数名相同、 函数参数不同、 分别位于派生类和基类中 -- 为 隐藏;(即与重载(overload)的区别是两个函数是否在同一个域(类)中)
关于隐藏的理解,在调用一个类的成员函数时,编译器会沿着类的继承链逐级向上查找函数的定义,如果找到了则停止查找;所以如果一个派生类和一个基类都有一个同名函数(不论函数参数是否相同),而编译器最终选择了在派生类中的函数,那么就说这个派生类的成员函数“隐藏”了基类的同名函数,即它阻止了编译器继续向上查找函数的定义。(所以对于上述的情形3,同名函数,虽函数参数不同,但位于派生类和基类中时,基类函数会审美观点屏蔽。) 参考链接:http://www.cppblog.com/lingyun1120/archive/2011/04/27/145135.html
补充一点:关于隐藏的情形2,相当于是重新定义了基类中non-virtual函数,这样其实并不好,详情可参考 《Effective C++》中条款36:绝不重新定义继承而来的non-virtual函数。
我们以上的情况,属于隐藏的第二种情况:函数名,函数参数相同,发生隐藏,调用子类的成员函数,下面我们执行一下第三种:
修改函数:
void Aclass::SameFunction(const int a)
{
qDebug()<<"进入Aclass::SameFunction构造函数";
qDebug()<<"数值:"<<a;
}
void Bclass::SameFunction()
{
qDebug()<<"进入Bclass::SameFunction构造函数";
}
显然,发生了隐藏,此隐藏和overload很像,函数重载会根据参数选择相应的函数,此处的隐藏,还是子类同名成员函数隐藏了父类成员函数
那么如果调用父类的同名成员函数:
Bclass B(1);
B.Aclass::SameFunction(14);
显然,加上作用域标识符自然解决问题。
那么以上发生的,都是最简单的情况,如果发生多态,情况还会有变化:
2发生多态
2.1重写
类声明:
class Aclass
{
public:
Aclass();
int sameItem;
virtual void SameFunction();
};
class Bclass : public Aclass
{
public:
Bclass(const int a);
int sameItem;
void SameFunction();
};
类函数定义:
Aclass::Aclass()
{
qDebug()<<"进入Aclass构造函数";
this->sameItem = 10;//父类构造,直接默认变量为10
}
Bclass::Bclass(const int a)
{
qDebug()<<"进入Bclass构造函数";
this->sameItem = a;//子类构造,需要赋值操作
}
void Aclass::SameFunction()
{
qDebug()<<"进入Aclass::SameFunction构造函数";
}
void Bclass::SameFunction()
{
qDebug()<<"进入Bclass::SameFunction构造函数";
}
具体操作:
Aclass * A = new Bclass(1);//父类指针指向子类对象,发生多态
A->SameFunction();
输出:
典型的多态,父类指针指向了子类的对象,此时,发生动态联编,调用的是子类的同名函数,父类的同名函数被隐藏
这个过程叫重写(override)!
2.2不重写
那么如果,父类不进行虚函数的声明,会发生什么?
类声明:
class Aclass
{
public:
Aclass();
int sameItem;
void SameFunction();
};
class Bclass : public Aclass
{
public:
Bclass(const int a);
int sameItem;
void SameFunction();
};
具体操作:
Aclass * A = new Bclass(1);//父类指针指向子类对象,发生多态
A->SameFunction();
输出:
所有的都一样,就是父类没有声明virtual,发生多态,也没办法,还是调用了父类中的同名函数,无法进行重写操作。
2.3缺失
class Aclass
{
public:
Aclass();
int sameItem;
// void SameFunction();
};
class Bclass : public Aclass
{
public:
Bclass(const int a);
int sameItem;
void SameFunction();
};
如果我们再次进行修改,父类确实同名成员函数,其余操作都不变
Aclass * A = new Bclass(1);//父类指针指向子类对象,发生多态
A->SameFunction();
程序出错!
3实例
题目1:
#include <iostream>
using namespace std;
class A
{
public:
virtual void print()
{
cout << "A::print()" << "\n";
}
};
class B: public A
{
public: virtual void print()
{
cout << "B::print()" << "\n";
}
};
class C: public A
{
public: virtual void print()
{
cout << "C::print()" << "\n";
}
};
void print(A a)
{
a.print();
}
int main()
{
A a, *aa, *ab, *ac;
B b;
C c;
aa = &a;
ab = &b;
ac = &c;
a.print();
b.print();
c.print();
aa->print();
ab->print();
ac->print();
print(a);
print(b);
print(c);
}
输出:
A::print() B::print() C::print() A::print() B::print() C::print() A::print() A::print() A::print()
题目2:
结构体也是一样的
struct A{
void foo(){printf("foo");}
virtual void bar(){printf("bar");}
A(){bar();}
};
struct B:A{
void foo(){printf("b_foo");}
void bar(){printf("b_bar");}
};
A *p=new B;
p->foo();
p->bar();
输出:
barfoob_bar
解释:
A *p=newB;// A类指针指向一个实例化对象B, B类继承A类,先调用父类的无参构造函数,bar()输出bar,B类没有自己显示定义的构造函数。此时发生多态,不是单纯的继承调用,注意!
p->foo();//执行B类里的foo()函数,因为foo不是虚函数,所以直接调用父类的foo函数,输出foo
p->bar();//执行B类的bar()函数, 该函数为虚函数,调用子类的实现,输出b_bar
(题目来源:牛客网,解答:@Ze)
题目3:
#include<iostream>
using namespace std;
class Base
{
public:
virtual int foo(int x)
{
return x * 10;
}
int foo(char x[14])
{
return sizeof(x) + 10;
}
};
class Derived: public Base
{
int foo(int x)
{
return x * 20;
}
virtual int foo(char x[10])
{
return sizeof(x) + 20;
}
} ;
int main()
{
Derived stDerived;
Base *pstBase = &stDerived;
char x[10];
printf("%d\n", pstBase->foo(100) + pstBase->foo(x));
return 0;
}
答案:2014
第一个foo:调用继承的函数,因为父函数有virtual修饰,被子类的同名函数覆盖(重写)。
第二个foo:调用父类函数,因为 父函数没有有virtual修饰。
此时发生多态,不是单纯的继承调用,注意!
(题目来源:牛客网)
4总结
发生多态,看父类,有虚函数就执行子类中的函数,如果没有虚函数,直接调用父类中的函数
不发生多态,子类会隐藏父类