对象指针被强转为void*后再delete,是不会调用对象的析构函数的。
1. 类的大小
空类大小为1 (因为需要一个占位符,否则创建对象时无法分配内存)
带有虚函数的类的大小要加一个虚函数指针的大小,32位下4
静态成员数据不属于类,所以类的大小不受虑静态成员数据影响
2.构造函数和析构函数
2.1 可以显示调用构造函数和析构函数,其作用就和普通函数一样,并不创建和销毁对象。
如:
b.B::B();
b.~B();
如果对象生命周期是存在于栈上的。也就是说,如何管理,是系统完成而程序员不能控制的。所以,即使我们调用了析构,在对 象 生命周期结束后,系统仍然会再调用一次析构函数,将其在栈上销毁,实现真正的析构。(这时候如果显示调用析构函数和构造 函数的话,容易造成崩溃或内存泄露).
如果是在堆上,则程序员可自己管理,重载new和delete,实现内存池。
2.2 构造函数调用顺序
类先调用父类的构造函数,再调用成员对象的构造函数,最后调用自身的构造函数。
成员对象的构造函数调用顺序,和成员对象的声明顺序一致.
2.3 显示定义了虚析构函数则不能定义默认析构函数,否则编译出错
3.构造函数参数列表
3.1不得不用参数列表初始化
如果类中有一个对象成员,而且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如下,如果不适用初始化列表初始化m_ct,则School无法创建对象,编译报错.
class CT
{
public:
CT(int x, int y, int iAge);
private:
int m_x;
int m_y;
int m_iAge;
};
class School
{
public:
School():m_ct(1, 20, 26)
{
}
CT m_ct;
};
3.2 对于较大的成员对象,提高效率
这个地方还没弄的彻底明白,稍后继续
拷贝构造函数和负值
#define MAXNAMELEN 24
class Complex
{
public:
Complex();
Complex(const Complex &cpl);
Complex operator +(Complex &c2);
Complex operator =(Complex &c2);
~Complex();
private:
int x;
int y;
char *m_pszMyName;
};
Complex::Complex()
{
x = 0;
y = 0;
m_pszMyName = (char *)malloc(MAXNAMELEN);
memset(m_pszMyName, '\0', MAXNAMELEN);
}
Complex::Complex(const Complex &cpl)
{
x = cpl.x;
y = cpl.y;
m_pszMyName = (char *)malloc(MAXNAMELEN);
memset(m_pszMyName, '\0', MAXNAMELEN);
memcpy(m_pszMyName, cpl.m_pszMyName, MAXNAMELEN);
}
Complex::~Complex()
{
free(m_pszMyName);
m_pszMyName = NULL;
}
Complex Complex::operator +(Complex &c2)
{
Complex c;
c.x = x + c2.x;
c.y += y + c2.y;
return c;
}
Complex Complex::operator = (Complex &cp1)
{
Complex c;
c.m_pszMyName = (char *)malloc(MAXNAMELEN);
c.x = cp1.x;
c.y = cp1.y;
return c;
}
这里注意:
Complex com1;
Complex com2;
com1 = com2; //调用赋值重载和拷贝构造函数,因为return c. //如果将赋值重载函数定义为Complex& operator =(Complex &c2);
//则只调用赋值重载,不会调用拷贝构造了
如果 Complex com1;
Complex com2 = com1; //这里是拷贝构造函数,而不是赋值,语义是用com1创建com2,和Complex com2(com1);是一样的
派生类的构造函数
派生类构造函数的一般形式为:
派生类构造函数名(总参数列表) : 父类构造函数名(参数列表) //如果不调用父类构造函数,则使用默认构造函数
{
}
//通常不希望使用默认构造函数
4. 与类相关的const
3.1 常对象
常对象的所有数据成员的值不能被修改;
在定义的时候使用构造函数初始化成员,如 const CT ct(1, 20, 26);
常对象只能调用常成员函数,不能调用非常成员函数(编译器无法保证数据不被修改)
3.2 常成员变量
只能使用构造函数初始化列表来初始化常成员,如:
CT::CT(int x, int y, int iAge) : m_iAge(iAge)
{
m_x = x;
m_y = y;
}
3.3 常成员函数
声明:<类型标志符>函数名(参数表)const;
(1)const是函数类型的一部分,在实现部分也要带该关键字。
(2)const关键字可以用于对重载函数的区分,所以在实现的时候要带const关键字。
(3)常成员函数不能更新类的成员变量,但是可以调用成员变量;
也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数。
5.this指针
memset(this, 0, sizeof(*this)),或者ZeroMemory() 本意是想对成员用0初始化,但是也会将虚函数表清0
如果一个类,有虚函数,当调用虚函数的时候,就会报空指针错误
6. 虚函数不能为内置函数.因为内联函数是不能在运行中动态确定其位置的。 即使虚函数在类的内部定义,
编译时,仍将其看作非内联的