1、类的定义:
类有点类似于C语言中的结构体,C语言中的结构体中只能定义变量而不能定义函数,但是C++中的结构体可以定义函数。而类相当于结构体一样,在一个作用域中可以定义函数和变量。
用下面的例子来了解一下类的定义
class classname
{
//里面存放的是成员函数和成员变量
}; //不要忘记分号
接下来我们分析一下类的定义:class 为定义类的关键字,classname代表的是类的名字,{}的范围代表类的作用域,里面存放的内容代表的是类的主体。类的最后是要加分号的。
类中的元素称为类的成员:类中的数据称为类的成员变量,类中的函数称之为成员函数或者是类的方法。
类的定义分为两种:
1.将函数的声明和定义都放在类的作用域中,如下所示:
//定义一个学生对象的类
class student
{
public:
//将类的定义和声明放在类中
void CoutInfo()
{
cout<<"姓名为:"<<_name<<" 性别为:"<<_sex<<"年龄为"<<age;
}
public:
char *_name;
char *_sex;
int age;
};
但是如果你将函数定义到类中的话,编译器可能将函数当作内联函数来处理。
2.第二种是将声明放在.h文件中,将定义放在.cpp文件中
student.h
//.h文件
class student
{
public:
//只给出声明
void CoutInfo()
public:
char *_name;
char *_sex;
int age;
};
stdent.cpp
//.cpp文件中
#include"student.h"
void student::CoutInfo()
{
cout<<"姓名为:"<<_name<<" 性别为:"<<_sex<<"年龄为"<<age;
}
看了上面例子,一般来说更加期望用第二种方法。
上面的例子中,你是不是发现了许多的看不懂的地方,比如public是什么?第二种方法中的函数名前面加的东西是什么?我们下面就来一一解释。
2、类的访问限定符及封装
我们来看一下类的分装特性:
封装:封装就是将数据和操作数据的方法进行有机的结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
这段话听完好像对封装也没有一个具体的概念,其实简单的举例来说就是:就像我们家中的电视机一样,电视机是将显示器、电路板等等结构都封装到一个盒子里,最后就是我们看到的电视机。将电视的所有的器件组装成电视机的过程就是封装。
C++中类将对象的属性和方法结合在一起,让对象更加的完善,通过访问权限选择性的将其接口提供给外部的用户使用。
3、访问限定符:
1.public 被public修饰的成员在类外可以直接访问(当你定义对象后,你可以直接访问和修改public修饰的成员)。如下所示:
//定义一个学生对象的类
class student
{
public:
//将类的定义和声明放在类中
void CoutInfo()
{
cout<<"姓名为:"<<_name<<" 性别为:"<<_sex<<" 年龄为"<<age;
}
public:
char *_name;
char *_sex;
int age;
};
int main()
{
student st;
st._name = "xiaoming";
st._sex = "man";
st.age = 19;
st.CoutInfo();
return 0;
}
2.protected 被protected修饰成员在类外不能直接被访问
3.private 被private修饰成员在类外不能直接被访问(private修饰的成员不能向上面例子中的作法来访问)
访问权限的作用域从该访问限定符出现的位置开始直到下一个访问符出现的为止
class访问的默认权限为private,结构体的访问默认权限是public。
1.为什么把结构体的默认访问权限给成公有的?
因为为了兼容C语言,为了使C语言的代码在C++的情况下依然可以使用
2.为什么有的类的有些成员给的访问限定符是 私有的?
因为提高代码的安全性,因为如果不给成私有的话,别人可以随意的修改你的数据。
3.如何在类外访问一个类中私有的成员变量?
第一种方法:给出一个共有的方法,让这个方法来访问私有的成员变量
第二种方法:使用指针的方式来访问私有的,定义一个类的指针然后经过类中私有的成员的偏移量李艾访问这个私有的成员变量。(这种方式不推荐,因为成员变量给成私有的代表不希望随便改变)。
class student
{
public:
//使用一个公有的方法来访问成员变量
char *Getname()
{
return _name;
}
//如果你也要修改的话,也可以给一个设置函数来修改
void Setname(char* name)
{
_name = name;
}
private:
char *_name;
char *_sex;
int age;
};
int main()
{
student s1;
s1.Setname("haha");
cout<<s1.Getname()<<endl;
return 0;
}
5.C++中的struct和calss的区别?
两个关键字都可以定义类,但是在类和对象的角度来说struct和calss的访问限定符struct是私有的,calss是公有的。
4、类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中,在类外定义成员变量的话,需要使用::作用域解析符来指明成员属于哪一个类域。如下所示:
class Date
{
public:
void SetDate(int year, int mouth, int day);
private:
int _year;
int _mouth;
int _day;
};
//在类外定义时,需要在函数的名字前面加类的名字和作用域解释符::
void Date::SetDate(int year, int mouth, int day)
{
_year = year;
_mouth = mouth;
_day = day;
}
int main()
{
Date d;
d.SetDate(1998,2,21);
return 0;
}
类中定义一个成员变量的话,这个变量在类中具有全局属性,相当于一个作用域的全局变量。
注意:在类中的成员函数中,最好不要将函数的形参的名字不要和成员变量的名字重复。代码举例:
class Date
{
public:
//如果形参和成员变量的名字相同时,你无法判断是谁给谁赋值。
void Setyear(int year)
{
year = year;
}
private:
int year;
};
5.类的实例化
用类的类型创建一个对象的过程叫做类的实例化。用一个例子来形容就是:你如果要制作一块电路板的话,你首先需要一张电路图,类就相当于电路图,对象相当于电路板。
电路图------------------制作--------------电路板
类 ----------------实例化-------------对象
这就是类的实例化。
一个类的大小有多大呢?
如果按照我们看到的话,应该是类中成员变量的大小+类中成员函数的大小。但是计算出的大小却不是这样:
class Date
{
public:
void SetDate(int year, int mouth, int day)
{
_year = year;
_mouth = mouth;
_day = day;
}
private:
int _year;
int _mouth;
int _day;
};
int main()
{
Date d;
d.SetDate(1998,2,21);
cout<<sizeof(d)<<endl; //输出的是12.
return 0;
}
为什么输出的大小好像只有成员变量的大小呢?好像没有加入函数的大小。如果你计算的类的大小大于类中成员变量之和的话,多出来的并不是其他的东西,而是进行内存对齐之后的结果。
如果一个类中没有成员变量的话,那么按照上面的做法,这个类的占的大小不就是0了吗?
如果一个类中没有成员变量的话,它的大小为1.这是为什么呢?因为编译器为了区分空类定义的几个不同的对象。如果为空类的大小为0 的话,假设用一个空类定义了多个对象,此时编译器就没有办法区别这几个对象了。
那么类对象在内存中如何布局的呢?如下所示:
其实一个类在内存中的就是这样存放的,类的大小就是该类中成员变量之和,当然也要进行内存对齐。那么成员函数放在了哪里呢,其实成员函数放在了公共的代码段。
让我们再简单的回顾一下内存对齐
首先是结构体的内存对齐的规则:
1.第一个成员在结构体偏移量为0的地址处。
2.其他的成员要对其到某个数字(对齐数)的整数倍的地址处。
3.结构体的总大小:最大对齐数(所有变量类型的最大者于默认对其参数取最小值)的整数倍。
4.如果嵌套了结构体的情况,嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数
7.this指针
首先来看一下下面的这段代码:
class Date
{
public:
void PrintDate()
{
cout<<_year<<"."<<_mouth<<"."<<_day<<endl;
}
void SetDate(int year, int mouth, int day)
{
_year = year;
_mouth = mouth;
_day =day;
}
private:
int _year;
int _mouth;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.SetDate (2016, 6,6);
d2.SetDate (2017, 7,7);
d1.PrintDate();
d2.PrintDate();
return 0;
}
当我们调用函数来修改对象时,一般都将对象地址的传过去,这样才可以指定修改某个对象,但是上面的函数调用的时候,d1调用函数的时候却没有传任何的指针过去,那么如何判断该函数改变的时d1还是对象d2呢?
其实C++编译器给每个成员函数增加了一个隐藏的指针参数,让该指针指向当前的对象,也就是指向调用该函数的对象,在函数中所用成员 变量的访问都是由该指针来完成的。只是这个指针的操作是对用户透明的。由编译器自动完成。上面的打印日期的函数,相当于下面的情况:
this指针的特性
1.this指针的类型:类的类型*const.(this指针是不可以改变的,也就是this指针不能再函数里进行this++或者this--的操作,如果this指针改变的话,就不再指向原来的对象了)
2.只能在“成员函数”的内部使用
3.时时刻刻指向当前的对象,不属于对象的一部分,不会影响sizeof()的结果。
4.this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过exc寄存器自动传递,不需要用户传递。
注意:当用类定义的指针为空时,this指针为空,这时这个指针可以访问类的方法但是不可以访问类中的变量,因为调用类中的方法时,是直接用方法来替换调用的语句,所以可以访问。