1、class member storage
sizeof ( class )
sizeof 求类的大小会发现,类的大小取决于数据成员的大小,而与成员函数没关系。成员函数并不用类的存储空间。
这就很纳闷了,成员函数不需要面子的嘛? 为啥不给朕空间呢?
#include <iostream>
using namespace std;
class time
{
public:
time(int h, int m, int s)
:hour(h),min(m),sec(s){}
void dis()
{
cout<<"this -->"<<this<<endl;
}
private:
int hour;
int min;
int sec;
};
class A
{
};
int main()
{
time t(1,2,3);
A a;
cout<<sizeof(A)<<endl; //A里边没数据 size为1 用一个占位符
cout<<"sizeof(time) = "<<sizeof(time)<<"\nsizeof(t) = "<<sizeof(t)<<endl;
//只与数据成员大小相关 成员函数不占用对象的空间
return 0;
}
用类去定义对象时, 系统会为每一个对象分配存储空间。
如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。如下:
但是在C++中,只用一段空间来存放这个共同的函数代码段,在调用各对象的函数时,都去调用这个公用的函数代码。
也就是说 :对象是有自己的空间,而成员函数的代码是同族类的对象共享的 不占用具体对象的空间,如下:
当然,这样做会大大节约存储空间。因此每个对象所占用的存储空间只是该对象的数据部分所占用的存储空间,而不包括函数代码所占用的存储空间。
那么就带来一个问题,如何确保对象1访问成员函数的时候调用的是对象1的数据成员呢?
------>this 指针 出场!
1.2 对象的this指针
int main()
{
time t1(1,2,3);
t1.dis(); //打印t1的this指针
cout<<"&t = "<<&t1<<endl;//t1的地址
puts("");
time t2(2,2,2);
t2.dis();
cout<<"&t1 = "<<&t2<<endl;
puts("");
time t3(3,3,3);
t3.dis();
cout<<"&t3 = "<<&t3<<endl;
return 0;
}
在对象调用的时候,向该函数传递了该对象的指针,&t 也就是this ,唯一确定。
(this 也就是 指在一个对象内的指针,this.val 偏移量)
***注意
(1)不论成员函数在类内定义还是在类外定义, 成员函数的代码段都用同一种方式存储。
也就是说,this在类内或者类外定义使用都可以,只要是此类的就?(类内声明 类外定义)。
(2)说 某对象的数据成员(物理真是存在的);某对象的成员函数(从逻辑角度看待)
成员函数不属于某某对象,从物理角度来说的。某对象的成员函数,这是逻辑角度来说。
2、const in class
const 修饰数据成员、成员函数、类对象
2.1const修饰数据成员(常数据成员)
const 修饰数据成员, 称为常数据成员, 可能被普通成员函数和常成员函数来使用, 不可以更改。
必须初始化, 可以在类中(不推荐), 或初始化参数列表中(这是在类对象生成之前唯一一次改变 const 成员的值的机会了)。
初始化列表, 一方面提高了效率, 另一方面解决一类因面向对象引入的一些新功能的特殊安排。
修饰数据成员时,只能在参数列表里初始化。被const修饰的数据成员 不能被修改。 :val(v)
class A
{
public:
A(int v = 0)
// :val(v) 可以修改
{
// val = v; NO ~
}
private:
const int val;
};
2.2 const 修饰成员函数
位置 修饰函数 放在哪块呢? void dis() const {…………} !声明和定义出都要有const
意义:const 承诺不会修改数据成员!从外边定义了的。。但是自己函数块内部的ok
在const修饰的成员函数里的调用的函数,能访问const和非const数据成员 但是不能修改非const数据成员
const 修饰的函数 只能 访问const成员函数 函数类型相同访问。
***构成重载 void display() const; void display() ;
const 对象只能调用const成员函数
非const成员对象优先调用非const成员函数;若无,则可调用const成员函数
class A
{
public:
A(int v = 1)
:val(v)
{ }
void display() const
{
cout<<"void display() const"<<endl;
}
void display()
{
cout<<"void display()"<<endl;
}
private:
const int val;
};
int main()
{
A a(5); //普通类型
a.display();
const A aa(5); //const 类型
aa.display();
return 0;
}
2.3修饰类对象
const 修饰函数 void func()const {} 是从函数的层面 不能修改数据
const 修饰对象(const A a;) 是从对象的层面 不修改数据 只能调用 const 成员函数
3.static in class
C语言中 static 修饰
(1)修饰全局变量-->使全局变量变为仅在本文件内部使用的全局变量
(2)修饰局部变量—>将局部变量生命周期扩展到全局变量 进程或main函数。BUT不改变作用域!局部变量还是局部变量
(3)修饰函数-->将全局函数变成了本文件内的全局函数
C++中 用于实现在同一个类中不同对象之间的数据共享,行为协调。
静态变量有全局变量的优势,又不会像全变量一样被滥用。而用于管理静态变量,就需要用到静态函数。类的静态成员,属于类,也属于对象,但终归属于类。
3.1 static修饰数据成员(静态变量)
C++ 在类内部 来实现 族类 对象间 的 数据共享
语法规则:类内定义,类外初始化,两种访问方式。
class A
{
public:
int x,y;
static int share; //类内声明
private:
};
//static 静态变量 初始化 only one
int A::share = 111;
//
int main ()
{
//访问 (1)类名::静态数据成员
cout<<A::share<<endl;
//(2)类对象.静态数据成员
A a,b;
cout<<a.share<<endl;
cout<<b.share<<endl;
}
注意:
1、只有在生成对象的时候,普通数据成员(x,y)才有空间。而static成员(share)在类声明的时候就已经开辟了空间(保存在data rw段)
2、static 数据成员既属于类也属于对象。但终归属于类 根本上还是类的。
3、共享:static实现了同族类之间对象的数据共享。
4、类大小:static 成员类外存储, 求类大小, 并不包含在内。
cout<<"sizeof(A) = "<<sizeof(A)<<endl;//只包含除static变量之外的数据大小
5、存储:static 成员是命名空间属于类的全局变量, 存储在 data 区 rw 段。
6、访问:可以通过类名访问(无对象生成时也可以 (1)), 也可以通过对象访问(2)。
3.2 static 修饰成员函数 (静态函数)
为了管理静态成员, C++提供了静态函数, 以对外提供接口。 并且静态函数只能访问静态成员。
静态成员函数只能访问静态数据成员。 原因: 非静态成员函数, 在调用时 this指针时被当作参数传进。 而静态成员函数属于类, 而不属于对象, 没有 this 指针。
4.const static in class
如果一个类的成员, 既要实现共享, 又要实现不可改变。那就用 static const 修饰。
修饰成员函数,格式并无二异。
修饰数据成员。 必须要类内部初始化。
具有const static 的双重性质:族类对象间可共享,数据值不可更改。
//修饰数据成员:必须要类内部初始化。
const static int data = 100;
5.指向类成员的指针
C++扩展了指针在类中的使用,使其可以指向类成员(数据成员和函数成员),这种行为是类层面的,而不是对象层面的。
基本语法:
非类指针
(1)int a; 指向数据成员
int *ps = &a;
(2)void func(); 指向成员函数
void (*pf)() = &func;
类中的指针初始化 合适的位置加 Stu::
(1) int Stu::*ps = &Stu::a;
数据类型 类名::*指针名 = &类名::变量名;
(2)void (Stu::*pf)() = &Stu::print;
函数返回类型 (类名::*函数指针名)(参数) = &类名::成员函数名;
解引用 对象用. 对象指针用->
(1)对象.*数据成员指针
对象指针->*数据成员指针
(2)(对象.*成员函数指针)(参数) (ps->*pf)();//函数
(对象指针->*成员函数指针)(参数)
指向类指针的应用举例:
#include <iostream>
using namespace std;
class Stu
{
public:
Stu(string sn,int sa)
:name(sn),age(sa){} //构造函数
void print()
{
cout<<name<<"----"<<age<<endl;
}
string name;
int age;
};
//指向类对象的指针
//数据成员 函数成员
int main()
{
//栈
Stu s1("zhangsan",21);
Stu s2("wangwu",32);
// Stu *ps = &s1;
// s1.print(); //old
string Stu::*pn = &Stu::name; //声明初始化。。定义的时候 和具体的类关联
cout<<s1.*pn<<"++++"<<s2.*pn<<endl; //解引用。。使用的时候 和具体的对象关联
void (Stu::*pf)() = &Stu::print; //指针指向成员函数
(s1.*pf)(); //application
//堆
Stu *ps = new Stu("nigulasi",99);
cout<<ps->age<<endl;//成员
(ps->*pf)();//函数
return 0;
}
注意:
string Stu::*pn = &Stu::name; 指针定义的时候 和具体的类关联
cout<<s1.*pn<<"++++"<<s2.*pn<<endl; 使用的时候 和具体的对象关联
指向类成员的指针, 具有指针的形而不具体指针的实, 或者, 确切意义上说, 不指是指针。
指向类成员的指针, 本质存放的不是地址, 存放的偏移量。
(this->*pf[sec])(val); //*******this指针应用。******
#include <iostream>
using namespace std;
class Widget
{
public:
Widget()
{
pf[0] =f;
pf[1] =g;
pf[2] =h;
pf[3] =i;
}
// int getCount()
// {
// return cnt;
// }
void select(int val, int sec) // 偏移量!
{
if(sec<0 && sec>3) return ;
(this->*pf[sec])(val); //*******this指针应用。
}
private:
void f(int val ) {cout<<"void f()"<<val<<endl;}
void g(int val ) {cout<<"void g()"<<val<<endl;}
void h(int val ) {cout<<"void h()"<<val<<endl;}
void i(int val ) {cout<<"void i()"<<val<<endl;}
enum {cnt =4};//设初值
void (Widget::*pf[4])(int); // 指 针 函 数 声 明
};
int main()
{
Widget w;
w.select(100,0);
// int count = w.getCount();
// cout<<count<<endl;
return 0;
}