空类的大小:一个字节,用来占位的,用来标识当前对象真实存在在内存中。当一个类中存在非静态成员变量时,空类占位用的一个字节就不会单独存在。
成员属性:属于对象的,当定义对象时,属性才会存在,才开辟对应空间,多个对象会存在多份成员属性,彼此独立互不干扰。
成员函数:属于类的(不占用对象空间),编译期存在,它的存在与否和是否定义对象无关。类成员函数只有一份,多个对象共享同一个函数。
1.类成员进阶
#include<iostream>
using namespace std;
class CTest
{
public:
int m_a;
CTest()
{
m_a = 1;
}
void fun()
{
cout << __FUNCTION__ <<" "<<m_a <<" ";
//show(); //和下面一样
this->show();
}
void show(/* CTest* const this */)
{
cout << "show" << endl;
}
};
void fun(CTest* pTst)
{
cout << __FUNCTION__ << " " << pTst->m_a << endl;
}
int main()
{
CTest tst;
cout << sizeof(tst) << " " << sizeof(CTest) << endl; //4 4
CTest tst2;
cout << &tst.m_a << " " << &tst2.m_a << endl; //010FF6E8 010FF6DC
tst.m_a = 10;
tst.fun(); //CTest::fun 10 show
tst2.m_a = 12;
tst2.fun(); //CTest::fun 12 show
fun(&tst); //fun 10
fun(&tst2); //fun 12
return 0;
}
下面认识一下this指针:
类中的非静态成员函数包括构造析构,会有一个默认的隐藏的参数this指针,它是编译器默认加上的,在所有参数之前,类型为当前类的指针,即:类 * const this。
当我们在用对象调用函数的时候,this指针就指向了调用的对象,在函数中使用类成员属性或其他类成员函数都是默认通过this指针调用的。
this指针的作用:this指针是一个指向当前对象的const指针,它允许在类的非静态成员函数中访问当前对象的所有成员变量和成员函数。
2.静态成员:
静态成员变量:
1、属于类的,不参与类对象空间的占用,编译期存在,所有对象共享这一份静态成员变量。其存在与否和是否定义对象无关,在构造函数中不能对其初始化(那个是赋值)。
2、静态成员属性需要在类外进行初始化,格式:类型 类名作用域变量名,给定初始化值
3、调用:1.通过对象去调用 2.类名作用域直接使用
静态成员函数 :
1、属于类的,不参与类对象空间的占用,编译期存在,所有对象共享这一份静态成员函数。
2、调用:1.通过对象去调用 2.类名作用域直接使用
例:
int CTest::m_b = 10; //定义及初始化
使用:1.通过对象去调用 2.类名作用域直接使用
#include<iostream>
using namespace std;
class CTest
{
public:
int m_a;
static int m_b;
CTest()
{
m_a = 1;
//m_b = 2; //不是初始化而是赋值
}
void fun()
{
cout << m_a << " " << m_b << endl;
funStatic2();
}
static void funStatic()
{
//cout << m_b << endl; //和下面一样
cout << CTest::m_b << endl;
//fun(); //普通函数不可以调用
funStatic2(); //静态函数可以调用
}
static void funStatic2()
{
cout << "funStatic2" << endl;
}
};
int CTest::m_b = 10; //定义及初始化
int main()
{
cout << "m_b=" << CTest::m_b << endl; //m_b=10
CTest tst;
cout << sizeof(CTest) << " " << sizeof(tst) << endl; //4 4
CTest tst2;
cout << &tst.m_a << " " << &tst2.m_a << endl; //006FF724 006FF718 所有对象各有一份普通成员变量,表现为内存地址不同
cout << &tst.m_b << " " << &tst2.m_b << endl; //000BC000 000BC000 所有对象共享这一份静态成员变量,表现为内存地址相同
tst2.fun(); //1 10
tst.m_b = 20;
cout << tst2.m_b << endl; // 20 一个对象被修改后 其余对象是用的就是被修改后的值
//区分
tst.m_a = 2;
cout << tst2.m_a << endl; //1
//以下两种表现形式输出结果相同
CTest::funStatic();
tst.funStatic();
return 0;
}
静态成员函数与普通成员函数的区别:
1.没有隐藏的this指针参数,也就不能使用普通的成员变量和函数,只能使用静态成员变量和函数,但在普通成员函数中可以使用静态成员。
2.静态成员函数是否通过对象都可以调用,而普通的成员函数只能通过对象去调用。
3.常量成员
这里我们先回忆一下之前在C语言中const相关知识:
int a;
const int b = 2;
/*指向不可改*/
int* const p1 = &a; //const修饰指针p1本身,指针的指向不能修改(p1指向了变量a,就不能在指向变量b了)
//p1 = &b; //error
*p1 = 3; //可以通过指针修改它指向的整形变量的值
/*内容不可改*/
const int* p2 = &a; //const修饰的是指针指向的整型空间,不能通过p2指针去修改,指针的指向是可以改的(可以指向a,也可以指向b)
p2 = &b;
//*p2 = 4; //error
/*指向不可改内容不可改*/
const int* const p3 = &b;
//*p3 = 4; //error
//p3 = &a; //error
1.常量的特性:定义就必须初始化,一旦初始化后值就不能修改(不能通过正常手段修改)
2.当类中有const类型的变量时,在定义的时候必须要初始化,这个初始化操作是在初始化参数列表中完成的,而构造函数的函数体代码中进行的操作严格来说是赋值,而并非初始化。先执行初 始化参数列表,再执行构造函数体代码。对于普通的变量来说也可在初始化参数列表中初始化。
#include<iostream>
using namespace std;
class CTest
{
public:
int m_a;
const int m_b;
//初始化参数列表:用来真正初始化对象中成员属性的,构造参数列表后加冒号,多个成员属性用逗号分隔
//使用圆括号做初始化,常量必须在这里初始化,变量均可
CTest() :m_a(2), m_b(3)
{
m_a = 1; //严格意义来说,这里是赋值 m_a可以在这里初始化
//m_b = 2; //严格意义来说,这里是赋值 m_b不可以在这里初始化
}
//初始化参数列表初始化成员的顺序取决于:成员在类中的声明顺序,而不是写在初始化参数列表中的顺序
CTest(int a) :m_b(a), m_a(m_b)
{
cout << a << endl;
}
};
int main()
{
cout << sizeof(CTest) << endl; //8
CTest tst;
cout << tst.m_a << " " << tst.m_b << endl; //1 3
tst.m_a = 10;
//tst.m_b = 10; //常量不可修改
CTest tst2(30); //30
cout << tst.m_a << " " << tst.m_b << endl; //10 3
return 0;
}
over!