1.reinterpret_cast,static_cast,dynamic_cast、const_cast区别
- reinterpret_cast,非常随意的一种类型转换方式,不做任何检查,非常危险,跟C的强制类型转换差不多
- static_cast,同C的隐示类型转换差不多,用做下行转换时不提供动态类型检查
- dynamic_cast,类层次上的转换,下行转换时提供动态类型检查,需要有虚函数表,转换失败返回nullptr
- const_cast,可以去掉类型的const与volatile属性
2.类的大小
- 空类,实例在内存中得有地址,编译器添1个字节
class CTest1
{
};
int size = sizeof(CTest1); // 1
- 普通类,计算大小时不算静态成员变量,只计算普通成员大小+虚表指针大小
class CTest2
{
public:
virtual void func() {}
static void func2() {}
protected:
static int c;
private:
int a;
char* p;
};
int size = sizeof(CTest2); // 12
- 普通派生类,大小为基类大小+自己大小
class CTest3 : public CTest2
{
private:
int c;
};
int size = sizeof(CTest3); // 16
- 虚继承的派生类,大小为基类大小+自己大小+虚基类表指针
class CTest4 : public CTest2
{
private:
int c;
};
int size = sizeof(CTest4); // 20
3.char指针
- char* 字符指针;字符串指针
- char** 指向char*的指针
- char a[] 字符数组,a表示数组首地址 char*
- char *a[] char*数组,a表示数组首地址 char**
char s[] = "test";
s[0] = 'a'; // ok
char* p = s;
p[0] = 'a'; // ok
p = "test";
p[0] = 'a'; // error
s = p; // error
char* a[] = {"test1", "test2"};
int size = sizeof(a); // 8
char** p1 = a; // ok
4.成员函数指针
- static函数不需要限定符CTest::
- 成员函数需要限定符CTest::,注意不是实例test::
class CTest
{
public:
virtual void func() { cout << "func" << endl; }
void func2() { cout << "func2" << endl; }
static void func3() { cout << "func3" << endl; }
};
typedef void(*Func)(void);
typedef void(CTest::*Func2)(void);
CTest test;
Func f = &CTest::func3;
f(); // func3
Func2 f2 = &CTest::func2;
(test.*f2)(); // func2
f2 = &CTest::func;
(test.*f2)(); // func
5.虚函数表
- 有虚函数的类都有虚函数表,类对象都有虚函数指针
- 多重继承情况下,有虚函数的基类都有虚函数表,所以派生类有多个虚函数表,派生类的虚函数声明放在第一个虚函数表中
- 虚函数表指针位于实例最前面
- 编译时确定虚函数表,构造对象时初始化虚函数表成员
- 通过虚函数表可以访问到private的virtual函数
class CTest5
{
private:
virtual void test() { cout << "test" << endl; }
};
typedef void(*Func)(void);
CTest5 test;
Func f = (Func)*((int*)(*(int*)(&test)) + 0);
f(); // test
class CBase
{
public:
virtual void test() { cout << "base test" << endl; }
};
class CDriver: public CBase
{
private:
virtual void test() { cout << "driver test" << endl; }
};
CBase *pb = new CDriver;
pb->test(); // driver test
CDriver *pd = dynamic_cast<CDriver *>(pb);
pd->test(); // error
6.深拷贝与浅拷贝
- 在成员变量包含指针时,需要注意浅拷贝会导致2个对象的指针指向同一片内存,释放时恨容易造成同一内存两次释放
class CTest6
{
public:
CTest6() { n = 10; p = new int(0); }
~CTest6() { delete p; }
CTest6(const CTest6& test) { p = new int; *p = *test.p; }
private:
int n;
int* p;
};
7.字节对齐
- 结构体每个成员的起始位置都应为自身大小的整数倍
- 结构体大小必须是结构体内最大成员的整数倍,不足则补齐
某些时候需要禁用字节对齐,如通信帧定义 #pragma pack(1)
struct MyStruct
{
int a; // offset 0-3 补4字节
double b; // offset 8-15
char c; // offset 16 补7字节
};
int size = sizeof(MyStruct); // 24
8.static修饰符
- 全局变量 限定作用域到声明此变量的编译单元
- 普通变量 此局部变量存储于静态存储区,更改了生命周期,不更改作用域
- 普通函数 同全局变量
- 成员变量 成员归属升级为类 除static const int外都需要在类定义体外定义
- 成员函数 同成员变量 static成员函数不能声明为const,const修饰的表明函数不会修改对象,static成员函数不属于对象
class CTest6
{
public:
CTest6() { n = 10; p = new int(0); }
~CTest6() { delete p; }
CTest6(const CTest6& test) { p = new int; *p = *test.p; }
static const int a = 1;
static const float f;
private:
int n;
int* p;
};
const float CTest6::f = 0.5f;
9.const修饰的成员函数调用非const成员函数
class CTest6
{
public:
CTest6() { n = 10; p = new int(0); }
~CTest6() { delete p; }
CTest6(const CTest6& test) { p = new int; *p = *test.p; }
static const int a = 1;
static const float f;
void test1()const { CTest6* p = const_cast<CTest6*>(this); p->test2(); }
void test2(){ cout << "test2" << endl; }
private:
int n;
int* p;
};
const float CTest6::f = 0.5f;
CTest6 test;
test.test1(); // test2