c++内存分布之虚函数(多继承)
- 系列
c++内存分布之虚函数(单一继承)
c++内存分布之虚函数(多继承) 【本文】
结论
1.虚函数表指针 和 虚函数表
- 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关。多一个父类,派生类就多一个虚函数表指针,同时,派生类的虚函数表就额外增加一个
- 1.2 派生类和父类同时含有虚函数,派生类的虚函数按照父类声明的顺序(从左往右),存放在继承的第一个父类中虚函数表后面,而不是单独再额外建立一张虚函数表
- 1.3 按照
先声明、先存储、先父类、再子类
的顺序存放类的成员变量 - 1.4 无论是派生类还是父类,当出现了虚函数(普通虚函数、虚析构函数、纯虚函数),排在内存布局最前面的一定是虚函数表指针
2.覆盖继承
其实,覆盖继承不够准确。
2.1 成员变量覆盖
- 派生类和父类出现了同名的成员变量时,派生类仅仅将父类的同名成员隐藏了,而非覆盖替换
- 派生类调用成员变量时,按照就近原则,调用自身的同名变量,解决了当调用同名变量时出现的二义性的现象
2.2 成员函数覆盖
需要考虑是否有虚函数的情况
存在虚函数的覆盖继承
父类和派生类出现了同名虚函数函数((普通虚函数、纯虚函数),派生类的虚函数表中将子类的同名虚函数的地址替换为自身的同名虚函数的地址-------多态出现
不存在虚函数的覆盖继承
父类和派生类同时出现同名成员函数,这与成员变量覆盖继承的情况是一样的,派生类屏蔽父类的同名函数
关于
- 演示环境: VS2017 + 32位程序
- 多继承(本文主要展开)
- 代码写的不够规范: 因为多态中,任何带虚函数的基类类的析构函数都应该是虚析构函数。但是我这里没有写出来,目的是缩短文章篇幅。
序号 | 情况(多继承,基类个数:2) |
---|---|
1 | 基类均无虚函数(A,派生类有虚函数,B,派生类不含有虚函数) |
2 | 两个基类中只有一个类有虚函数(A,派生类有虚函数,B,派生类不含有虚函数) |
3 | 两个基类中都含有虚函数(A,派生类有虚函数,B,派生类不含有虚函数) |
1.基类均无虚函数
1.1 基类均无虚函数,派生类有虚函数
1.1.1 代码
// 基类
class baseA
{
public:
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
virtual void print() { std::cout << "deriveA::print()\n\n\n"; }
int _me = 3;
int _mf = 4;
};
1.1.2 内存分布
1>class deriveA size(28):
1> +---
1> 0 | {vfptr}
1> 4 | +--- (base class baseA)
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &deriveA::print
- 因为派生类存在虚函数,故排在最前的是虚函数表指针(此时,虚函数表指针属于派生类,而非基类),接着在世基类成员变量,这里先是基类baseA,然后才是基类baseB,最后才是派生类成员变量。基类成员变量存储顺序与声明顺序有关(从靠近派生类的基类开始,先基类baseA,然后是基类baseB)。这个顺序与之前总结的规律一致: 先基类再派生类,先声明,先存储。
- 虚函数表。由于只有派生类存在虚函数,故虚函数表中只有派生类的虚函数地址。
1.1.3 sizeof
28字节 = 基类和派生类总共六个int成员变量 + 虚函数表指针4字节
1.1.4 监视结果
1.2 基类无虚函数,派生类也没有虚函数
1.2.1 代码
// 基类
class baseA
{
public:
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
int _me = 3;
int _mf = 4;
};
1.2.2 内存布局
1>class deriveA size(24):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | _ma
1> 4 | | _mb
1> | +---
1> 8 | +--- (base class baseB)
1> 8 | | _mc
1>12 | | _md
1> | +---
1>16 | _me
1>20 | _mf
1> +---
- 虚函数表指针。因为不存在虚函数,故没有虚函数表指针表和虚函数表。
- 同样的,靠近派生类的是baseA,然后才是B,故按照先声明先存储的顺序,先是基类baseA的成员变量,然后是基类baseB的成员变量,最后才是派生类的成员变量。
1.2.3 sizeof
24字节= 基类总4个int成员变量 + 派生类的2个int成员变量 = 4 * 4 + 4 * 2 = 24
1.2.4 监视结果
2.两个基类中只有一个类有虚函数
2.1 两个基类中只有一个类有虚函数,派生类有虚函数
2.1.1 代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
virtual void print() { std::cout << "virtual deriveA::print() \n\n"; }
int _me = 3;
int _mf = 4;
};
2.1.2 内存布局
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1> 1 | &deriveA::print
- 虚函数表指针。
- 由于基类和派生类都存在虚函数,故存在虚函数表指针。
- 内存布局顺序:先基类,再派生,先声明先存储。代码中,基类baseA存在虚函数且基类baseA先声明,所以,{vfptr}的位置放在了基类baseA中,且排在第一位,然后才是基类baseA的成员变量,接着是基类baseB的成员变量,最后才是派生类的成员变量。
- 虚函数表。
- 存储虚函数的虚函数表。本例中,虚函数只有基类baseA和派生类才有,故,按照先声明,先存储的顺序。依次为:&baseA::show,&deriveA::print。
2.1.3 sizeof
28字节 = 基类总共4个int成员变量 + 派生类的2个int成员变量 + 一个虚函数表指针4字节
2.1.4 监视结果
2.1.5 交换baseA和baseB的顺序呢?
- 代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseB, public baseA
{
public:
virtual void print() { std::cout << "virtual deriveA::print() \n\n"; }
int _me = 3;
int _mf = 4;
};
- 内存分布
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1> 1 | &deriveA::print
1>
1>deriveA::print this adjustor: 0
你肯定也发现了。这与2.1.2内存布局的情况是一摸一样的。
存储顺序:先基类,再派生,先声明,先存储。由于存在虚函数,所以,这个需要优先考虑。
尽管baseB更靠近派生类,但是baseA的优先级更高,因为基类baseA存在虚函数而基类baseB不存在。
虚表依然仅按照先声明像存储的顺序存储虚函数。基类优先级 > 派生类优先级。
2.1.5.3 sizeof
与 2.1.3 sizeof相同。因为没有成员变量增加和减少。
2.2 两个基类中只有一个类有虚函数,派生类没有虚函数
2.2.1 代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
int _me = 3;
int _mf = 4;
};
2.2.1 内存分布
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
- 虚函数表指针。 因为只有基类baseA存在虚函数,故需要第一个考虑虚函数表指针。 且基类baseA先声明。
- 虚函数表,因为只有基类baseA存在虚函数,故虚函数表只保存了基类的虚函数地址。
- 存储顺序: 先基类,再派生,先声明像存储。所以;最先是baseA,然后是baseB,最后才是派生类。
2.2.2 交换基类顺序呢?
- 代码
class deriveA : public baseB, public baseA
{
.....
}
- 内存分布
1>
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
- 你肯定又发现了,这与上面的内存分布情况是一样的。
- 为什么?因为基类有两个,且只有其中一个存在虚函数,所以,按照先声明存储的规则,且考虑到有虚函数的基类的优先级大于没有虚函数的基类。故,含有虚函数的类成员变量排在内存分布图的虚函数表指针的后面,而且是紧挨着。
2.2.3 sizeof
2.2.4 监视结果
3.两个基类中都含有虚函数
3.1 两个基类中都含有虚函数,派生类有虚函数
3.1.1 代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
virtual void print() { std::cout << "deriveA::print()\n\n"; }
int _me = 3;
int _mf = 4;
};
3.1.2 内存分布
1>class deriveA size(32):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | _me
1>28 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1> 1 | &deriveA::print
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
1>
1>deriveA::print this adjustor: 0
- 等等,你是不是也发现了,内存模型中出现了2个{vfptr}。一个属于基类baseA,另一个属于基类baseB。
- 虚函数表。 发现了吧:1.派生类的虚函数表多了一张表,2.派生类的虚函数是放在第一张虚函数表中。
- 按照先前的顺序:先基类,再派生,先声明,先存储。但是有虚函数的类要优先考虑。这里基类baseA和baseB还有派生类都含有虚函数。那么先看基类,按照先声明先存储的顺序,baseA基类相对baseB基类先声明,故基类baseA的虚函数表指针首先被存储,接着再是基类baseA的成员变量,然后是基类baseB的虚函数表指针,基类baseB的成员变量。最后是派生类。
- 为什么派生类的虚函数是追加在第一张虚表的后面? 请看下面的一段汇编(没学过汇编,不献丑)结论: 派生类的虚函数是追加在第一张虚表的后面。当需要使用派生类的虚函数是,用第一张表的虚函数表指针指向派生类的虚函数即可。(个人观点)下面的汇编也应该是这样:1,找到虚函数表的起始地址,2.找到派生类的虚函数偏移,3.使用虚函数表指针指向派生类的虚函数。
deriveA *pda = &da;
00A7A02E lea eax,[ebp-28h]
00A7A031 mov dword ptr [ebp-34h],eax
pda->print();
00A7A034 mov eax,dword ptr [ebp-34h]
00A7A037 mov edx,dword ptr [eax]
00A7A039 mov esi,esp
00A7A03B mov ecx,dword ptr [ebp-34h]
00A7A03E mov eax,dword ptr [edx+4]
00A7A041 call eax
00A7A043 cmp esi,esp
00A7A045 call 00A714C4
3.1.3 sizeof
32字节 = 基类总共4个int成员变量 + 派生类的2个int变量 + 2个虚函数表指针 = 4 * 4 + 2 * 4 + 2 * 4 = 32
3.1.4 监视结果
监视结果中看不到派生类的虚函数存储情况。
3.2 两个基类中都含有虚函数,派生类没有虚函数
3.2.1 代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
// 基类
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 1;
int _md = 2;
};
// 派生类
class deriveA : public baseA , public baseB
{
public:
int _me = 3;
int _mf = 4;
};
3.2.2 内存布局
1>class deriveA size(32):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | _me
1>28 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
- 虚函数表指针。基类baseA先于基类baseB声明,两个基类同时含有虚函数,但是先声明,先存储。故先存储基类baseA的虚函数表指针。
- 虚函数表。 多继承,故每张表各自存储自己的虚函数表的信息。
3.2.3 sizeof
3.2.4 监视结果
4.扩展继承3个基类
4.1 3个基类都有虚函数, 派生类没有虚函数
4.1.1 代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
int _me = 3;
int _mf = 4;
};
4.1.2 内存布局
1>class deriveA size(44):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | +--- (base class baseC)
1>24 | | {vfptr}
1>28 | | _mh
1>32 | | _mi
1> | +---
1>36 | _me
1>40 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
1>
1>deriveA::$vftable@baseC@:
1> | -24
1> 0 | &baseC::listening
4.2 三个基类中每个基类都有虚函数,派生类也有虚函数
4.2.1 代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
virtual void show() { std::cout << "virtual deriveA::show() \n\n"; }
int _me = 3;
int _mf = 4;
};
4.2.2 内存布局
1>class deriveA size(44):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | +--- (base class baseC)
1>24 | | {vfptr}
1>28 | | _mh
1>32 | | _mi
1> | +---
1>36 | _me
1>40 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &deriveA::show
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
1>
1>deriveA::$vftable@baseC@:
1> | -24
1> 0 | &baseC::listening
1>
1>deriveA::show this adjustor: 0
- Note: 派生类的虚函数是放在第一张虚函数表中的。
4.3 三个基类中其中2个基类都有虚函数,另一个基类没有虚函数,派生类没有虚函数
4.3.1 代码
// 基类
class baseA
{
public:
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
int _me = 3;
int _mf = 4;
};
4.3.2 内存布局
1>class deriveA size(40):
1> +---
1> 0 | +--- (base class baseB)
1> 0 | | {vfptr}
1> 4 | | _mc
1> 8 | | _md
1> | +---
1>12 | +--- (base class baseC)
1>12 | | {vfptr}
1>16 | | _mh
1>20 | | _mi
1> | +---
1>24 | +--- (base class baseA)
1>24 | | _ma
1>28 | | _mb
1> | +---
1>32 | _me
1>36 | _mf
1> +---
1>
1>deriveA::$vftable@baseB@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseB::play
1>
1>deriveA::$vftable@baseC@:
1> | -12
1> 0 | &baseC::listening
4.4 三个基类中其中2个基类都有虚函数,另一个基类没有虚函数,派生类有虚函数
4.4.1 代码
// 基类
class baseA
{
public:
// virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
virtual void show() { std::cout << "virtual deriveA::show() \n\n"; }
int _me = 3;
int _mf = 4;
};
4.4.2 内存分布
1>
1>class deriveA size(40):
1> +---
1> 0 | +--- (base class baseB)
1> 0 | | {vfptr}
1> 4 | | _mc
1> 8 | | _md
1> | +---
1>12 | +--- (base class baseC)
1>12 | | {vfptr}
1>16 | | _mh
1>20 | | _mi
1> | +---
1>24 | +--- (base class baseA)
1>24 | | _ma
1>28 | | _mb
1> | +---
1>32 | _me
1>36 | _mf
1> +---
1>
1>deriveA::$vftable@baseB@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseB::play
1> 1 | &deriveA::show
1>
1>deriveA::$vftable@baseC@:
1> | -12
1> 0 | &baseC::listening
1>
1>deriveA::show this adjustor: 0
- Note: 派生类的虚函数存放在第一张虚函数表中。
c++内存分布之虚函数(多继承)的更多相关文章
- c++内存分布之虚函数(单一继承)
系列 c++内存分布之虚函数(单一继承) [本文] c++内存分布之虚函数(多继承) 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...
- C++解析(25):关于动态内存分配、虚函数和继承中强制类型转换的疑问
0.目录 1.动态内存分配 1.1 new和malloc的区别 1.2 delete和free的区别 2.虚函数 2.1 构造函数与析构函数是否可以成为虚函数? 2.2 构造函数与析构函数是否可以发生 ...
- 【整理】C++虚函数及其继承、虚继承类大小
参考文章: http://blog.chinaunix.net/uid-25132162-id-1564955.html http://blog.csdn.net/haoel/article/deta ...
- c/c++: c++继承 内存分布 虚表 虚指针 (转)
http://www.cnblogs.com/DylanWind/archive/2009/01/12/1373919.html 前部分原创,转载请注明出处,谢谢! class Base { pu ...
- C++浅析——继承类内存分布和虚析构函数
继承类研究 1. Code 1.1 Cbase, CTEST为基类,CTest2为其继承类,并重新申明了基类中的同名变量 class CBase { public: int Data; CBase() ...
- c++ 对象内存分配和虚函数
1. c++类对象(不含虚函数)在内存中的分布 c++类中有四种成员:静态数据.非静态数据.静态函数.非静态函数. 1. 非静态数据成员放在每个对象内部,作为对象专有的数据成员 2. 静态数据成员被抽 ...
- c++内存分布之虚析构函数
关于 本文代码演示环境: VS2017+32程序 虚析构函数是一种特殊的虚函数,可以知道,虚函数影响的内存分布规律应该也适用虚析构函数.看看实际结果. Note,一个类中,虚析构函数只能有一个. 本文 ...
- C++对象的内存分布和虚函数表
c++中一个类中无非有四种成员:静态数据成员和非静态数据成员,静态函数和非静态函数. 1.非静态数据成员被放在每一个对象体内作为对象专有的数据成员. 2.静态数据成员被提取出来放在程序的静态数据 ...
- C#中的虚函数及继承关系
转载:http://blog.csdn.net/suncherrydream/article/details/8423991 若一个实例方法声明前带有virtual关键字,那么这个方法就是虚方法. 虚 ...
随机推荐
- CF1578I Interactive Rays:ICPC WF Moscow Invitational Contest I 题解
题意简述:在平面上有一个坐标 \((x_c,y_c)\) 和半径 \(r\) 都是整数的圆 \((1\leq r_c\leq \sqrt{x_c^2+y_c^2}-1)\),你可以询问不超过 \(60 ...
- SQL-join(inner join)、left join、right join、full join
0.JOIN 类型 有时我们需要从两个或更多的表中获取结果,数据库中的表可通过键将彼此联系起来.每个表中都有一个主键,主键(Primary Key)是一个列,值都唯一.这样做的目的是在不重复每个表中的 ...
- Spark(七)【RDD的持久化Cache和CheckPoint】
RDD的持久化 1. RDD Cache缓存 RDD通过Cache或者Persist方法将前面的计算结果缓存,默认情况下会把数据以缓存在JVM的堆内存中.但是并不是这两个方法被调用时立即缓存,而是 ...
- Hive(二)【数据类型、类型转换】
目录 一.基本数据类型 案例实操 二.集合数据类型 案例实操 Map类型 三.类型转换 1.隐式类型转换 2.显示(强制)类型转换 一.基本数据类型 HIVE MySQL JAVA 长度 例子 TIN ...
- Bitmaps与优化
1.有效的处理较大的位图 图像有各种不同的形状和大小.在许多情况下,它们往往比一个典型应用程序的用户界面(UI)所需要的资源更大. 读取一个位图的尺寸和类型: 为了从多种资源来创建一个位图,Bitma ...
- Java Spring 自定义事件监听
ApplicationContext 事件 定义一个context的起动监听事件 import org.springframework.context.ApplicationListener; imp ...
- Spring AOP通过注解的方式设置切面和切入点
切面相当于一个功能的某一个类,切入点是这个类的某部分和需要额外执行的其他代码块,这两者是多对多的关系,在代码块处指定执行的条件. Aspect1.java package com.yh.aop.sch ...
- Mongodb集群调研
目录 一.高可用集群的解决方案 二.MongoDB的高可用集群配置 三.Mongo集群实现高可用方式详解 四.Sharding分片技术 一.高可用集群的解决方案 高可用性即HA(High Availa ...
- Linux中的正则
目录 一.匹配规则 二.举例 一.匹配规则 * 匹配 0 或多个字符 ? 匹配任意一个字符 [list] 匹配 list 中的任意单一字符 [^list] 匹配 除list 中的任意单一字符以外的字符 ...
- ciscn_2019_es_7
这是我第一次见到srop的用法的题目,于是在此记录方便以后的复习 拿到程序例行检查 将程序放入ida中 可以看到栈的大小是0x10,却可以显示出0x30的内容,所以我们可以通过这个溢出泄露出/bin/ ...