目录
1.类的引入
2.类的定义
3.访问限定符
4.类的作用域
5.类对象模型
6.类的大小
1.类的引入
C语言结构体中只能定义变量,在C++中,结构体不仅可以定义变量,也可以定义函数。
C++兼容C语言,结构用法可以继续使用
同时sruct也升级成了类
struct Stack
{
int* a;
int top;
int capacity;
};
int main()
{
struct Stack s1;
Stack s2;
return 0;
}
我们可以用两种方式定义结构体,s1是C语言的。s2是C++的
上面那串的Stack是类名,有了类名就可以作下面的类型
还有一个成员函数,C语言的初始化都要放在外面去写,现在我们还可以在类里面写成员函数了,而且不用传这个参数了,成员变量它直接就能访问
struct Stack
{
void Init()
{
a = nullptr;
top = capacity = 0;
}
int* a;
int top;
int capacity;
};
int main()
{
Stack s2;
s2.Init();
return 0;
}
还有比如说push我们也可以类似上面一样的写法
s2.Push(1);
2.类的定义
class className
{
};
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略;
那么class和struct有什么区别呢
class引入了访问限定符这一类的概念
3.访问限定符
访问限定符分为了三个1.public(公有)2.protected(保护)3.private(私有)
1.public修饰的成员在类外可以直接被访问
2.protected和private的成员在类外不能直接被访问
3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4.如果后面没有访问限定符,作用域就到}即类结束
5.class的默认访问权限private,structural为public(因为struct要兼容C);
class Stack
{
void Init()
{
a = nullptr;
top = capacity = 0;
}
void Push(int x)
{
if (top == capacity)
{
size_t newcapacity = capacity == 0?4:capacity * 2;
a = (int*)realloc(a,sizeof(int) * newcapacity);
capacity = newcapacity;
}
a[top++] = x;
}
int* a;
int top;
int capacity;
};
int main()
{
Stack s2;
s2.Init();
s2.Push(1);
return 0;
}
这里是无法被访问的,那么我们为什么要引入class呢
cout << s2.a[s2.top] << endl;
cout << s2.a[s2.top-1] << endl;
有人以为可以访问栈顶元素是可以这样子搞,但是它不知道是指向栈顶元素还是指向栈顶元素的下一个位置
实际上指向的是栈顶元素的下一个位置,所以C++就不希望别人这样随意的访问
class Stack
{
public:
void Init()
{
a = nullptr;
top = capacity = 0;
}
void Push(int x)
{
if (top == capacity)
{
size_t newcapacity = capacity == 0?4:capacity * 2;
a = (int*)realloc(a,sizeof(int) * newcapacity);
capacity = newcapacity;
}
a[top++] = x;
}
int Top()
{
return a[top - 1];
}
private:
int* a;
int top;
int capacity;
};
int main()
{
Stack s2;
s2.Init();
s2.Push(1);
cout << s2.Top() << endl;
return 0;
}
这样子通过private和public的限定我们就能让你访问到我想让你访问到的和不想让你访问到的,比如说你想访问栈顶元素,我们在类里面通过操作来反馈给你就大大减少了出错的可能性了
但是我们上面一个public去掉就跑不动了,因为class默认是私有的
类里面不受访问限定符的限制,限制的是类外面的
类的两种定义方式
1.声明和定义全部放在类体中,需注意,成员函数如果在类中定义,编译器可能会将其当成内联函数处理
2.类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名
这里是.h文件里面的
class Stack
{
public:
void Init();
void Push(int x);
int Top();
private:
int* a;
int top;
int capacity;
};
这里是.cpp文件里面的
void Stack::Init()
{
a = nullptr;
top = capacity = 0;
};
void Stack::Push(int x)
{
if (top == capacity)
{
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
a = (int*)realloc(a, sizeof(int) * newcapacity);
capacity = newcapacity;
}
a[top++] = x;
};
int Stack::Top()
{
return a[top - 1];
};
这里加Stack::是为了区分全局函数和成员函数,还可以使用类域里面的成员变量和成员函数(编译器默认去全局域里面找)
注意:类里面没有向上查找这种说法,类是类域范围里面都可以使用的,你在上面使用一个变量,它会在类域这个范围内查找
这里我们引入一个日期类
class Date
{
public:
void Init(int year, int month, int day)
{
year = year;
month = month;
day = day;
}
private:
int year;
int month;
int day;
};
int main()
{
Date d;
d.Init(2022, 7, 19);
}
这里就涉及到域搜索的知识,一个是局部域的year,一个是类域的year,局部优先,所以是自己赋值给自己,所以好区分我们通常可以这样子写
int _year;
int _month;
int _day;
C++标准并没有这么写,看你的个人意愿,就形成了风格
4.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用::作用域操作符指明成员属于哪个类域
这个我们在上面的内容也有间接提到,不做过多的解释
5.类对象模型
用类类型创建对象的过程,称为类的实例化
1.类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间类储存它;
2.一个类可以实例化出多个对象,实例化的对象,占用实际的物理空间,存储类成员变量
我们解除私有,我们能不能这样访问
int main()
{
Date::_year;
Date._year;
}
这样是不行的,因为我们上面的年月日是声明不是定义,声明没有给它开空间不能访问
Date d;
这才是定义,定义开出来空间上面的空间就都开出来了我们就可以访问它了
而且我们不能直接去访问,因为是私有的,我们要通过间接的方式去访问
d._year=1;
这样是不行的
类就像一个图纸,我们只有建出来才是实体
6.类的大小
我们来看这个类的大小
cout << sizeof(Date) << endl;
cout << sizeof(d) << endl;
我们可以看见函数的大小是不包括在里面的,为什么呢
Date d1;
d1.Init(1,2,3);
Date d2;
d2.Init(4, 5, 6);
如果我们在每个对象中都存一个这样一个函数肯定是不划算的,就像是我们小区里面的公共空间这个小区里面都可以使用,但是像年月日就是自己房子里面的家具,自己使用
所以类里面的函数都存在公共代码区,所以每次使用的时候都会去这个区域里面使用
这一点我们通过汇编代码也可以得到证实
我们看一下类中仅有成员函数和空类的大小
class A2
{
public:
void f2(){}
};
class A3
{
};
int main()
{
cout << sizeof(A2) << endl;
cout << sizeof(A3) << endl;
}
有成员函数和没有成员函数是没有区别的,成员函数也不存在对象里面
这里涉及到占位的知识
C++对于这种只有成员函数和空类,分配1byte,不存储数据,只是占位
设想一下我们如果连一个byte位都不给它分配我们怎么区别,不定义过怎么证明这个对象存在
A2 a1;
A2 b1;
cout <<& a1 << endl;
cout << &b1 << endl;
类和对象的知识和指针一样都起到承上启下的作用,而且类和对象的知识也是很多的,学好了类和对象才好理解下面的STL知识等等,如果写的不好欢迎大家指出