2021-02-01C/C++学习00
请注意,本文编写于 666 天前,最后修改于 204 天前,其中某些信息可能已经过时。

目录


1. 没有虚继承

1.1.1 单个类

顺序保存类中的成员

c
# include <iostream>
using namespace std;
/*
单类不带虚函数的类对象内存结构
*/
class CA
{
public:
	int m_n1 = 0x11111111;
	int m_n2 = 0x22222222;
};


int main()
{
	CA a;
	return 0;
}
....

单类不带虚函数的类对象内存结构

1.1.2 单个类、有虚函数

第一个四个字节是虚函数指针(__vfptr),指向虚函数表(vftable),也就是保存虚函数的地址,接着是存放类成员

c
# include <iostream>
using namespace std;

//单类带虚函数的类对象内存结构
class CA
{
public:
	virtual void Test() {}
	int m_n1 = 0x11111111;
	int m_n2 = 0x22222222;
};

int main()
{
	CA a;
	return 0;
}

单类带虚函数的类对象内存结构

1.1.3 单继承

先是基类成员,接着是派生类成员

c
# include <iostream>
using namespace std;

//单虚继承不带虚函数的类对象内存结构
class CA
{
public:
	int m_n1 = 0x11111111;
	int m_n2 = 0x22222222;
};
class CB :public CA
{
public:
	int m_n3 = 0x33333333;
	int m_n4 = 0x44444444;
};

int main()
{
	CB b;
	return 0;
}

单继承不带虚函数的类对象内存结构

1.1.4 单继承、有虚函数

第一个4字节是虚函数指针,指向基类虚函数表,接着是基类的成员,和派生类成员依次排列

c
# include <iostream>
using namespace std;

class CA
{
public:
	virtual void Test() {}
	int m_n1 = 0x11111111;
	int m_n2 = 0x22222222;
};
class CB :public CA
{
public:
	int m_n3 = 0x33333333;
	int m_n4 = 0x44444444;
};


int main()
{
	CB b;
	return 0;
}

单继承带虚函数的类对象内存结构

1.1.5 多重继承

c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
	int m_n2 = 0x22222222;
};
class CB
{
public:
	int m_n3 = 0x33333333;
	int m_n4 = 0x44444444;
};
class CC :public CA, public CB
{
public:
	int m_n5 = 0x55555555;
	int m_n6 = 0x66666666;
};

int main()
{
	CC c;
	return 0;
}

多重继承不带虚函数的类对象内存结构

幺蛾子

  1. 子类转父类指针,安全
  2. 父类转子类指针,不安全,可能会发生越界
c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_nA = 0xAAAAAAAA;
};

class CB
{
public:
	int m_nB = 0xBBBBBBBB;
};

class CD :public CA, public CB
{
public:
	int m_nD = 0xDDDDDDDD;
};


int main()
{
	CD d;
	CA* p1 = &d;		//没问题
	CB* p2 = &d;		//没问题,编译器计算了偏移,将p2指向了对象中B的部分
	p2->m_nB = 0xEEEEEEEE;

	CD* p3 = (CD*)p2;	//没问题,编译器计算了偏移,但不建议
	p3->m_nA = 0x99999999;

	CB b;
	CD* p4 = (CD*)&b;		//危险,越界访问
	p4->m_nA = 0x88888888;
	p4->m_nB = 0x77777777;
	p4->m_nD = 0x66666666;
	return 0;
}

1.1.6 多重继承,有虚函数

  1. 如果派生类重写了基类的虚函数,将会放到继承到的基类的虚函数表中,也就是只会修改基类的虚函数表
  2. 如果基类没有虚函数的话,则后边的有虚函数的基类的成员会上前
  3. 如果子类添加了新的虚函数,则会放到第一个有虚函数基类的虚函数表中。
  4. 如果基类没有虚函数,则该基类没有虚函数指针和虚函数表

第一种情况

c
# include <iostream>
using namespace std;

class CA
{
public:
	virtual void TestA() { cout << "CA::TestA" << endl; }
	int m_n1 = 0x11111111;
};
class CB
{
public:
	virtual void TestB() { cout << "CB::TestB" << endl; }
	int m_n2 = 0x22222222;
};
class CC :public CA, public CB
{
public:
	int m_n3 = 0x33333333;
};

int main()
{
	CC c;
	return 0;
}

多重继承带虚函数的类对象内存结构(一)

第二种情况,派生类重写基类虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
	virtual void TestA() { cout << "CA::TestA" << endl; }
	int m_n1 = 0x11111111;
};
class CB
{
public:
	virtual void TestB() { cout << "CB::TestB" << endl; }
	int m_n2 = 0x22222222;
};
class CC :public CA, public CB
{
public:
	virtual void TestA() { cout << "CC::TestA" << endl; }
	virtual void TestB() { cout << "CC::TestB" << endl; }
	int m_n3 = 0x33333333;
};

int main()
{
	CC c;
	return 0;
}

多重继承带虚函数的类对象内存结构(二)

第三种情况,子类有多个虚函数
子类有多个虚函数,如果子类新增了虚函数,那么就会放到第一个基类的虚函数表中

c
# include <iostream>
using namespace std;

class CA
{
public:
	virtual void TestA() { cout << "CA::TestA" << endl; }
	int m_n1 = 0x11111111;
};
class CB
{
public:
	virtual void TestB() { cout << "CB::TestB" << endl; }
	int m_n2 = 0x22222222;
};
class CC :public CA, public CB
{
public:
	virtual void TestA() { cout << "CC::TestA" << endl; }
	virtual void TestB() { cout << "CC::TestB" << endl; }
	virtual void TestC() { cout << "CC::TestC" << endl; }
	int m_n3 = 0x33333333;
};


int main()
{
	CC c;
	return 0;
}

多重继承带虚函数的类对象内存结构(三)

第四种情况,如果第一个基类没有虚函数

  • 第二个基类到最前
c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
};
class CB
{
public:
	virtual void TestB() { cout << "CB::TestB" << endl; }
	int m_n2 = 0x22222222;
};
class CC :public CA, public CB
{
public:
	virtual void TestA() { cout << "CC::TestA" << endl; }
	virtual void TestB() { cout << "CC::TestB" << endl; }
	virtual void TestC() { cout << "CC::TestC" << endl; }
	int m_n3 = 0x33333333;
};

int main()
{
	CC c;
	return 0;
}

多重继承带虚函数的类对象内存结构(四)

2. 有虚继承

虚继承时,虚基类指针__vbptr指向虚基类表vbtable,虚基类表中存放的就是数据相对于虚基类指针的偏移

2.1.1 单虚继承

c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
};

class CB :virtual public CA
{
public:
	int m_n2 = 0x22222222;
	int m_n3 = 0x33333333;
};


int main()
{
	CB b;
	return 0;
}

单虚继承不带虚函数的类对象内存结构

2.1.2 单虚继承,有虚函数

  1. 如果基类没有虚函数,则基类没有虚函数表
  2. 派生类重写的虚函数会放到继承自基类的虚函数表中,派生类新增的虚函数会放到自己的虚函数表中(对象首的虚函数表)
  3. 如果派生类没有新加虚函数,则派生类没有自己的虚函数表(对象首的虚函数表)
c
# include <iostream>
using namespace std;

class CA
{
public:
	virtual void TestA() {}
	int m_n1 = 0x11111111;
};

class CB :virtual public CA
{
public:
	int m_n2 = 0x22222222;

};


int main()
{
	CB b;
	return 0;
}

单虚继承带虚函数的类对象内存结构

派生类重写基类虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
	virtual void TestA() {}
	int m_n1 = 0x11111111;
};

class CB :virtual public CA
{
public:
	virtual void TestA() {}
	int m_n2 = 0x22222222;
};

int main()
{
	CB b;
	return 0;
}

派生类重写基类函数

派生类有多个虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
    virtual void TestA() {}
	int m_n1 = 0x11111111;
};

class CB :virtual public CA
{
public:
	virtual void TestA() {}
	virtual void TestB() {}
	int m_n2 = 0x22222222;
};


int main()
{
	CB b;
	return 0;
}

派生类有多个虚函数

虚基类没有虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
};
class CB :virtual public CA
{
public:
	virtual void TestA() {}
	virtual void TestB() {}
	int m_n2 = 0x22222222;
};

int main()
{
	CB b;
	return 0;
}

虚基类没有虚函数

2.1.3 菱形继承

c
# include <iostream>
using namespace std;

class CA
{
public:

	int m_nA = 0xAAAAAAAA;
	virtual void TestA() {}
};

class CB :virtual public CA
{
public:
	int m_nB = 0xBBBBBBBB;
	virtual void TestB() {}
};
class CD :virtual public CA
{
public:
	int m_nD = 0xDDDDDDDD;
	virtual void TestD() {}
};

class CE :public CA, public CD
{
public:

	int m_nD = 0xEEEEEEEE;
};


int main()
{
	CE e;

	return 0;
}

菱形继承不带虚函数的类对象内存结构

2.1.4 菱形继承,有虚函数

  1. 如果基类没有虚函数,则没有虚函数表
  2. 派生类重写的虚函数,放到继承的基类虚函数表中
  3. 派生类新加的虚函数,放到第一个虚函数表中
c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
	virtual void TestA() {}
};
class CB :virtual public CA
{
public:
	int m_n2 = 0x22222222;
	virtual void TestB() {}
};
class CC :virtual public CA
{
public:
	int m_n3 = 0x33333333;
	virtual void TestC() {}
};
class CD :public CB, public CC
{
public:

	int m_n4 = 0x44444444;
};


int main()
{
	CD d;
	return 0;
}

菱形继承带虚函数的类对象内存结构

派生类重写基类虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
	virtual void TestA() {}
};
class CB :virtual public CA
{
public:
	int m_n2 = 0x22222222;
	virtual void TestB() {}
};
class CC :virtual public CA
{
public:
	int m_n3 = 0x33333333;
	virtual void TestC() {}
};
class CD :public CB, public CC
{
public:
	virtual void TestA() {}
	virtual void TestB() {}
	virtual void TestC() {}
	int m_n4 = 0x44444444;
};


int main()
{
	CD d;

	return 0;
}

派生类重写基类虚函数

派生类多一个虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
	virtual void TestA() {}
};
class CB :virtual public CA
{
public:
	int m_n2 = 0x22222222;
	virtual void TestB() {}
};
class CC :virtual public CA
{
public:
	int m_n3 = 0x33333333;
	virtual void TestC() {}
};
class CD :public CB, public CC
{
public:
	virtual void TestA() {}
	virtual void TestB() {}
	virtual void TestC() {}
	virtual void TestD() {}
	int m_n4 = 0x44444444;
};

int main()
{
	CD d;

	return 0;
}

派生类多一个虚函数

如果虚基类没有虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
};
class CB :virtual public CA
{
public:
	int m_n2 = 0x22222222;
	virtual void TestB() {}
};
class CC :virtual public CA
{
public:
	int m_n3 = 0x33333333;
	virtual void TestC() {}
};
class CD :public CB, public CC
{
public:
	virtual void TestB() {}
	virtual void TestC() {}
	virtual void TestD() {}
	int m_n4 = 0x44444444;
};

int main()
{
	CD d;
	return 0;
}

如果虚基类没有虚函数

如果第一个父类没有虚函数

c
# include <iostream>
using namespace std;

class CA
{
public:
	int m_n1 = 0x11111111;
};
class CB :virtual public CA
{
public:
	int m_n2 = 0x22222222;
};
class CC :virtual public CA
{
public:
	int m_n3 = 0x33333333;
	virtual void TestC() {}
};
class CD :public CB, public CC
{
public:
	virtual void TestB() {}
	virtual void TestC() {}
	virtual void TestD() {}
	int m_n4 = 0x44444444;
};

int main()
{
	CD d;
	return 0;
}

如果第一个父类没有虚函数

本文作者:Na1r

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!