一、c++中的类即可用class也可用struct
(1)类中的变量成为类中的属性或成员变量;
(2)类中的函数成为方法或类中的行为或成员函数;
1、类的定义:
法1:类的声明和定义全部放在类体中
class className { // 类体:由函数和变量组成 }; // 一定要注意后面的分号class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。类中的元素称为类的成员;类中的数据称为类的属性或者类的成员变量;类中的函数称为类的方法或者类的成员函数
法2:把类的声明和定义分开 :类的声明放在.h文件中,类的定义放在.cpp文件中:类里面的成员放到类外进行定义,需要加上类名::
.h
#pragma once struct student { void SetInfo(char *name, int age, char *gender); void PrintStudent(); char _name[20]; int _age; char _gender[3]; };
.c
# include"student.h" void student::SetInfo(char *name, int age, char *gender) { } void student::PrintStudent() { }
(1)防止头文件被重复多次引用的方式:1'#pragma once 2'#ifndef __头文件名__ #define __头文件名__ #endif
这两种方式的区别为:
2、类的作用域:
类定义了一个新的作用域,类的所有成员都必须处在类的作用域中。形参表和函数体处于类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。在类的作用域外,只能够通过对象(或指针)借助成员访问操作符.和->来访问类成员,跟在访问操作符后面的名字必须在相关联类的作用域中。
namespace N { int a = 10; } int a = 20; class Test { public: void FunTest(int a) { a = a; } void PrintA() { cout << a << endl; } private: int a; }; int main() { int a = 40; Test t; t.FunTest(30); cout << N::a << endl;// 打印命名空间里的 cout << a << endl;//函数里面没有就去全局作用域查找,若是全局作用域也没有 t.PrintA();//打印随机值,因为参数和返回值相同,不确定是哪个给哪个赋值 return 0; }
函数里面有a就去函数里面找, 第三个打印随机值,解决方法:给成员变量的名字前面加上下划线加以区分。
注意:以下程序中,此时两个FunTest函数没有在同一作用域中,不构成重载。
void FunTest() { } class Test { public: void FunTest(int a) { a = a; } }
3、类的实例化
用类类型创建对象的过程,称为类的实例化
(1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
(2. 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间存储类成员变量
(3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
4、类的对象模型:类中各成员在内存中的布局形式
什么是类?什么是类对象?类对象中包含哪些成员?计算一个类的大小。
类实例化为对象
存储方式一:
缺陷:每个对象中成员变量是不同的,但是函数都是相同的,如果一个类创建多个对象,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。没有采用方式一。
存储方式二:多给一个指针,存放成员函数表的首地址
方式二明显比方式一节省了太多的空间,但对象中还是多了一个指针。采用这种方式,最终的大小比实际大小多一个指针的的大小,但是VS编译后最终的大小没有那个指针的大小(只有成员变量的大小之和,内存对齐),所以没有采用这种方式。没有采用方式二。
存储方式3:没有存储指针,成员变量与成员函数分开,只用保存成员变量,不用管成员函数,将成员函数单独保存,编译器编译结束后,记得函数的入口地址,找到该函数。当没有成员变量,只有成员函数(空类)时,大小默认为1(在g++和VS编译环境下)。(空类大小为什么是1?定义三个不同的对象,若是空类的大小为0,则这三个对象都在地址0处,与三个不同的对象冲突。)采用了方式三。
问题1:函数只有一份,通过不同的对象来调用函数,函数体的里面每一个成员变量我们并不知道是属于哪一个对象,但是函数调用完毕,可以正确的把参数的信息设置到某一个对象上去,是怎么实现的?(C语言中的结构体不能定义函数)
问题2:成员变量在成员函数的后面,成员函数仍然可以正确的调用
答:(问题2的答案与问题1的大同小异)this指针。看起来成员变量前面什么都没有,实际上,在成员变量的前面存在一个this指针,谁调用这个函数this指针指向谁。this指针是编译器自动加上的,不用程序员手工添加。this指针通过ecx寄存器传递。this指针指向当前对象,不能作为左值,所以this不会被赋空。 编译器编译时会对代码进行修改:
1、识别类名 2、识别类中的成员变量 3、识别成员函数并对成员函数进行修改:先该函数原型,再改函数体:先修该函数的参数列表this指针指向类对象 (当前调用这个函数的对象) struct student { public: void SetInfo(char *name, int age, char *gender) { strcpy(_name, name); _age = age; strcpy(_gender, gender); } /*void SetInfo(student *this, char *name, int age, char *gender) { strcpy(this->_name, name); this->_age = age; strcpy(this->_gender, gender); }*/ void PrintStudent() { cout << _name << " " << _age << " " << _gender << endl; } /*void PrintStudent(student *this) { cout << this->_name << " " << this->_age << " " << this->_gender << endl; }*/ private: int _a; char _name[20]; int _age; char _gender[3]; };
二、面向对象(C++)的三大特性:(1)封装:函数是封装的一种形式:函数中的语句被封装在函数本身这个更大的实体,被封装的实体隐藏了它们的实现细节,可以调用该函数但是不能够访问函数中的语句
封装:隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互,将数据和操作数据的方法进行有机结合。如:函数(将多条语句封装到一块)
1’. public成员在类外可以直接访问
2'. protected和private成员在类外(在此可将protected和private理解成private)不能够访问
3'. 它们的作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4'. class的默认访问权限是private,而struct为public型(因为struct要兼容C)
类内:成员在类的花括号里面;
类外:成员在类的花括号外面
访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
思考题:如何在类外访问一个类中私有的成员变量?
1’、添加公有的方法来在类外访问一个类中私有的成员变量。(一般给成员变量前面加上下划线,用此来区分成员变量和外部的变量。)
struct student { public: void SetInfo(char *name, int age, char *gender) { strcpy(_name, name); _age = age; strcpy(_gender, gender); } void PrintStudent() { cout << _name << " " << _age << " " << _gender << endl; } void SetA(int a) { _a = a; } int GetA() { return _a; } private: int _a; char _name[20]; int _age; char _gender[3]; }; int main() { int a; student s1, s2; s1.SetA(10); s1.GetA(); s1.SetInfo("将旧", 28, "女"); s2.SetInfo("卡卡", 3, "男"); s1.PrintStudent(); s2.PrintStudent(); return 0; }
2‘、友元函数
3'、通过指针操作类的成员变量,必须知道类的结构(类如何布局),知道类的类型,这样才知道按照什么方式进行解析。(char *)((int)&s1+offsetof(student,_name))
struct student { public: void SetInfo(char *name, int age, char *gender) { strcpy(_name, name); _age = age; strcpy(_gender, gender); } void PrintStudent() { cout << _name << " " << _age << " " << _gender << endl; } private: int _a; char _name[20]; int _age; char _gender[3]; }; int main() { int a; student s1, s2; int *pa = (int *)&s1; *pa = 100; s1.SetInfo("将旧", 28, "女"); s2.SetInfo("卡卡", 3, "男"); s1.PrintStudent(); s2.PrintStudent(); return 0; }
(2)继承:
(3)多态: