c++对象内存布局
这篇文章我要简单地讲解下c++对象的内存布局,虽然已经有很多很好的文章,不过通过实现发现有些地方不同的编译器还是会有差别的,希望和大家交流。
在没有用到虚函数的时候,C++的对象内存布局和c语言的struct是一样的,这个比较容易理解,本文只对有虚函数的情况作分析,大致可以从以下几个方面阐述,
1. 单一继承
2. 多重继承
3. 虚继承
下面循序渐进的逐个分析,环境是ubuntu 12.04.3 LTS+gcc4.8.1
单一继承
为了实现运行时多态,虚函数的地址并不能在编译期决定,需要运行时通过虚函数表查找实际调用的虚函数地址。单一继承主要要弄明白两个问题:
1. 虚函数表在哪里?
2. 基类和派生类的虚函数是按照怎样的规则组织的?
类结构代码如下:
public:
base(int bv):bval(bv){};
virtual void base_f(){cout<<"base::f()"<<endl;}
virtual void base_g(){cout<<"base::g()"<<endl;}
private:
int bval;
};
class derived: public base{
public:
derived(int bv, int dv):base(bv), dval(dv){};
void base_f(){cout<<"derived::f()"<<endl;}
virtual void derived_h(){cout<<"derived::h()"<<endl;}
private:
int dval;
};
使用以下测试代码查看基类和派生类的内存空间:
FUN fun;
int **pvtab = (int**)&b;
cout<<"[0]:base->vptr"<<endl;
for(int i=0; i<2; i++){
fun = (FUN)pvtab[0];
cout<<" "<<i<<" ";
fun();
}
cout<<" 2 "<<pvtab[0][2]<<endl;
cout<<"[1]:bval "<<(int)pvtab[1]<<endl;
derived d(10, 100);
pvtab = (int**)&d;
cout<<"[0]:derived->vptr "<<endl;
for(int i=0; i<3; i++){
fun = (FUN)pvtab[0];
cout<<" "<<i<<" ";
fun();
}
cout<<" 3 "<<pvtab[0][3]<<endl;
cout<<"[1]:bval "<<(int)pvtab[1]<<endl;
cout<<"[2]:dval "<<(int)pvtab[2]<<endl;
运行结果:
[0]:base->vptr
0 base::f()
1 base::g()
2 1919247415
[1]:bval 10
[0]:derived->vptr
0 derived::f()
1 base::g()
2 derived::h()
3 0
[1]:bval 10
[2]:dval 100
用图表示就是这样的(本文中属于同一个类的函数和数据成员我都用同一种颜色表示,以便区分)。
结果可以看出:
1. 虚函数表指针在对象的第一个位置。
2. 成员变量根据其继承和声明顺序依次排列。
3. 虚函数根据继承和声明顺序依次放在虚函数表中,派生类中重写的虚函数在原来位置更新,不会增加新的函数。
4. 派生类虚函数表最后一个位置是NULL,但是基类是一个非空指针(红色字体显示),我不明白这个位置在gcc中的作用,有知道的朋友可以告诉我,非常感谢!
多重继承
如果加上多重继承,情况会有那么一点点复杂了,多个基类的虚函数如何组织才能使用任意一个基类都可以达到多态效果?
类结构代码如下:
public:
base1(int bv):bval1(bv){};
virtual void base_f(){cout<<"base1::f()"<<endl;}
virtual void base1_g(){cout<<"base1::g()"<<endl;}
private:
int bval1;
};
class base2{
public:
base2(int bv):bval2(bv){};
virtual void base_f(){cout<<"base2::f()"<<endl;}
virtual void base2_g(){cout<<"base2::g()"<<endl;}
LeetCode:3Sum, 3Sum Closest, 4Sum - tenos 阅读原文»
Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
For example, given array S = {-1 2 1 -4}, and target = 1.The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
我们可以在 2sum问题 的基础上来解决3sum问题,假设3sum问题的目标是target。每次从数组中选出一个数k,从剩下的数中求目标等于target-k的2sum问题。这里需要注意的是有个小的trick:当我们从数组中选出第i数时,我们只需要求数值中从第i+1个到最后一个范围内字数组的2sum问题。
我们以选第一个和第二个举例,假设数组为A[],总共有n个元素A1,A2....An。很显然,当选出A1时,我们在子数组[A2~An]中求目标位target-A1的2sum问题,我们要证明的是当选出A2时,我们只需要在子数组[A3~An]中计算目标位target-A2的2sum问题,而不是在子数组[A1,A3~An]中,证明如下:
假设在子数组[A1,A3~An]目标位target-A2的2sum问题中,存在A1 + m = target-A2(m为A3~An中的某个数),即A2 + m = target-A1,这刚好是“对于子数组[A3~An],目标位target-A1的2sum问题”的一个解。即我们相当于对满足3sum的三个数A1+A2+m = target重复计算了。因此为了避免重复计算,在子数组[A1,A3~An]中,可以把A1去掉,再来计算目标是target-A2的2sum问题。
对于本题要求的求最接近解,只需要保存当前解以及当前解和目标的距离,如果新的解更接近,则更新解。算法复杂度为O(n^2);
2 public:
3 int threeSumClosest(vector<int> &num, int target) {
4 int n = num.size();
5 sort(num.begin(), num.end());
6 int res, dis = INT_MAX;
7 for(int i = 0; i < n - 2; i++)
8 {
9 int target2 = target - num, tmpdis;
10 int tmpres = twoSumClosest(num, i+1, target2);
11 if((tmpdis = abs(tmpres - target2)) < dis)
12 {
13 res = tmpres + num;
14 dis = tmpdis;
15 if(res == target)
16 return res;
17 }
18 }
19 return res;
20 }
21
22 int twoSumClosest(vector<int> &sortedNum, int start, int target)
23 {
24 int head = start, tail = sortedNum.size() - 1;
25 int res, dis = INT_MAX;
26 while(head < tail)
27 {
28 int tmp = sortedNum[head] + sortedNum[tail];
29 if(tmp < target)
30 {
31 if(target - tmp < dis)
32 {
33 res = tmp;
34 dis = target - tmp;
35 }
36 head++;
37 }
38 else if(tmp > target)
39 {
40 if(tmp - target < dis)
41 {
42 res = tmp;
43 dis = tmp - target;
44 }
45 tail--;
46 }
47 else
48 return target;
49 }
50 return res;
51 }
52 };
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
- Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
- The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
为了避免重复,对于排序后的数组,当我们枚举第一个数时,如果遇到重复的就直接跳过;当我们找到一个符合的二元组(第二个数和第三个数)时,也分别对第二个数和第三个数去重。具体见代码注释。代码中的两个函数也可以合并成一个。
没有评论:
发表评论