文章目录
1,基类和派生类的转换
1.1,定义两个有虚函数的基类,一个派生类实现这两个基类的虚函数
#include <iostream>
class A {
public:
virtual void fun_a() = 0;
};
class B {
public:
virtual void fun_b() = 0;
};
class C :public A, public B{
public:
void fun_a() override final {
std::cout << "fun_a" << std::endl;
}
void fun_b() override final {
std::cout << "fun_b" << std::endl;
}
};
1.2,向下转型(即用基类指针指向派生类类型)
class D {
private:
A *m_a;
B *m_b;
public:
void fun(C *pC) {
//C派生类指针指向C派生类对象
m_a = pC;//从派生类C转换出基类A的部分
//m_a = dynamic_cast<A*>(pC);//没有必要
m_a->fun_a();
m_b = pC;//从派生类C转换出基类B的部分
//m_b = dynamic_cast<B*>(pC);//没有必要
m_b->fun_b();
}
};
int main() {
C c;
D d;
d.fun(&c);
return 0;
}
1.3,向下转型(当基类指针指向基类类型时,需要注意)
class D {
private:
A *m_a;
B *m_b;
public:
void fun(A *pA) {
//A基类指针指向C派生类对象
m_a = pA;//从A基类指针指向的C派生类转换出A基类的部分
//m_a = dynamic_cast<A*>(pC);//没有必要
m_a->fun_a();
//m_b = pA;//B基类类型指针无法指向A基类类型指针指向的C派生类
m_b = dynamic_cast<B*>(pA);//动态将A基类类型指针指向B基类类型(都指向C派生类对象)
m_b->fun_b();
}
};
int main() {
C c;
D d;
d.fun(&c);
return 0;
}
2,类的空指针问题
类中的函数被编译器静态编译了,所有非虚函数都可以调用,因为函数地址在编译期间已经确定。我们知道,类中的成员函数都是通过this指针调用成员变量的,编译器会将this指针作为默认参数传给类成员函数的,如myclass.function(int a,int b) --> function(&myclass,int a,int b)
编译后,成员函数传入了空的this指针,没有调用this指针则不出错,而后者调用了就会出错。调用成员函数的时候,函数地址是编译期间确定的,成员函数不通过对象指针(也即当前的p指针)去调用,对象指针仅仅作为参数传入函数然后去调用成员变量。
那如果是虚函数呢,因为虚函数要通过this指针计算vptr,然后找到vtable,然后dispatch。因为this指针为空,所以在找vtable时候就会coredump了。总之这类情况下,一切调用了this指针的函数都会出错,而完全不调用this指针的成员函数则没问题。
2.1,定义一个有数据成员的类,一个成员函数访问数据成员,一个不访问
#include <iostream>
class A {
private:
int m_data;
public:
virtual void fun_a(){
std::cout << "A fun_a" << std::endl;
}
void print(){
std::cout << "A data:" << m_data << std::endl;
}
};
class C :public A{
public:
void fun_a() override final {
std::cout << "C fun_a" << std::endl;
}
};
2.2,类的空指针可以操作不访问类成员变量的类成员函数
int main() {
C *pC;
//C *pC=nullptr;//效果和C *pC一样都是空的类指针
pC->fun_a();
return 0;
}
2.2,类的空指针不可以操作访问类成员变量的类成员函数
int main() {
//C *pC;
C *pC=nullptr;
pC->fun_a();
pC->print();
return 0;
}
int main() {
C c;
C *pC= &c;
pC->fun_a();
pC->print();
return 0;
}
3,基类和派生类转化深入
3.1,定义基类和派生类
#include <iostream>
#include <sstream>
class A {
private:
int m_data_a = 1;
public:
virtual void fun_a(){
std::cout << "A fun_a:" << m_data_a << std::endl;
}
};
class B {
private:
int m_data_b = 2;
public:
virtual void fun_b(){
std::cout << "B fun_b:" << m_data_b << std::endl;
}
};
class C :public A, public B{
private:
int m_data = 3;
public:
void fun_a() override final {
std::cout << "C fun_a:" << m_data << std::endl;
}
void fun_b() override final {
std::cout << "C fun_b:" << m_data << std::endl;
}
};
3.2,转换分析
类转换 | 运行结果 | 说明 |
“向上上转型”(即派生类指针或引用类型转换为其基类类型),本身就是安全的,尽管可以使用dynamic_cast进行转换,但这是没必要的, 普通的转换已经可以达到目的,毕竟使用dynamic_cast是需要开销的。 | ||
dynamic_cast转换失败返回0,pC是一个C派生类类型的空指针。而fun_a()和fun_b()都要访问成员变量,所以会段错误。如果fun_a()和fun_b()都不访问成员变量,还是可以正常执行的 | 对于“向下转型”有两种情况。 一种是基类指针所指对象是派生类类型的,这种转换是安全的; 另一种是基类指针所指对象为基类类型,在这种情况下dynamic_cast在运行时做检查,转换失败,返回结果为0; | |
编译出错。dynamic_cast将指向C派生类对象的基类类型指针pA装换成C派生类类型的指针后,又转换成了A基类类型的指针,用A基类类型的指针调用B基类的函数当然会出错 | ||
pC是一个指向C派生类类型的A基类类型指针。注意代码中注释有问题! | ||
基类类型指针可以指向派生类类型指针 |