目录
一、面向过程和面向对象初步认识
二、类的定义
1.类的两种定义方式
三、类的访问限定符及封装
1.访问限定符
四、封装
五、类的作用域
六、类的实例化
七、类对象模型
1. 计算类对象的大小
2.类对象的存储方式猜测
八、this指针
1. this指针的引出
2. this指针的特性
面向过程:关注:点餐、接单、送餐过程、关注流程函数的实现。面向对象:关注:用户、商家、骑手。关注对象之间关系。
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class Stack
{
public:
void Init() //Init可能会被当成内联函数处理
{
a = 0;
top = capacity = 0;
}
void Push(int x)
{
// ...
}
void Pop()
{
// ...
}
private:
int* a;
int top;
int capacity;
};
int main()
{
Stack st;
st.Init();
st.Push(1);
st.Push(2);
st.Push(3);
}
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
//f.h
class Queue
{
public:
void Init();
void Push(int x);
void Pop();
private:
QueueNode* head;
QueueNode* tail;
};
//f.cpp
void Queue::Init()
{
head = tail = nullptr;
}
void Queue::Push(int x)
{}
void Queue::Pop()
{}
类的定义:1.小函数,想成为 inline ,直接在类里面定义即可2.如果是大函数,应该声明和定义分离
成员变量命名规则的建议:
1.单词和单词之间首字母大写间隔 ----- 驼峰法 GetYear
2.单词全部小写,单词之间_分割 get_year
驼峰法:
a、函数名、类名等所有单词首字母大写 DateMgr
b、变量首字母小写,后面单词首字母大写 dateMgr
c、成员变量,首单词前面加_ _dateMgr
//一般都建议这样
class Date
{
public:
void Init(int year)
{
_year = year;
}
private:
int _year;
};
// 或者这样
class Date
{
public:
void Init(int year)
{
mYear = year;
}
private:
int mYear;
};
1. public 修饰的成员在类外可以直接被访问2. protected 和 private 修饰的成员在类外不能直接被访问 ( 此处 protected 和 private 是类似的 )3. 访问权限 作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止4. 如果后面没有访问限定符,作用域就到 } 即类结束。5. class 的默认访问权限为 private , struct 为 public( 因为 struct 要兼容 C)
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节(private/protected 成员变量),仅对外公开接口(public 成员函数)来和对象进行 交互。封装本质上是一种管理,让用户更方便使用类 。
因此C语言---> 没办法封装 既可以规范使用函数访问数据 也可以直接访问数据 --- 不规范C++ ---> 封装 必须规范使用函数访问数据 不能直接访问数据
class Person
{
public:
void PrintPersonInfo(); // 声明
private:
char _name[20]; // 声明
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo() // 定义
{
cout << _name << " " << _gender << " " << _age << endl;
}
用类类型创建对象的过程,称为类的实例化
class A
{
public:
void PrintA()
{
cout << _a << endl;
}
void func()
{
cout << "void A::func()" << endl;
}
private:
char _a;
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
运行结果为1,可知类的大小计算仅针对成员变量,成员函数不参与计算。
class A
{
public:
void PrintA()
{
cout << _a << endl;
}
void func()
{
cout << "void A::func()" << endl;
}
char _a;
};
int main()
{
A aa1;
A aa2;
aa1._a = 1;
aa2._a = 2;
aa1.PrintA();//函数PrintA()的地址存储在公共代码区
aa2.PrintA();
return 0;
}
我们可以了解到不同实例化的对象成员所调用的成员函数的地址是相同的,因此类中成员函数的地址是存储在公共代码区上的,编译链接时就根据函数名去公共代码区找到函数的地址(call函数地址)
结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
//类中仅有成员函数
class A2
{
public:
void f2() {}
};
//类中什么都没有---空类
class A3
{};
int main()
{
cout << sizeof(A2) << endl;
cout << sizeof(A3) << endl;
没有成员变量的类对象,给1byte,占位不存储实际数据,标识对象存在
A2 aa2;
A2 aaa2;
cout << &aa2 << endl;
cout << &aaa2 << endl;
return 0;
}
class Date
{
public:
void Print(Date* const this)
{
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year; // 年 -> 声明
int _month; // 月
int _day; // 日
};
int main()
{
Date d1;
d1.Init(2022, 7, 17);
Date d2;
d2.Init(2022, 7, 18);
d1.Print();
d2.Print();
return 0;
}
void Init(int year, int month, int day)
{
//this = nullptr;//error this指针被const修饰
cout << this << endl;
this->_year = year;
this->_month = month;
this->_day = day;
}
注:
实参和形参位置不能显示传递和接收this指针,但是可以在成员函数内部使用this指针
注:
this指针相当于函数形参,所以它存储在栈区中
VS下面传递this指针,是通过ecx寄存器传递的,这样this访问变量提高效率
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行 -- ok
// 2.this指针存在哪里? 堆 栈 静态区 常量区 -- 栈,因为他是一个形参
class A
{
public:
void PrintA()
{
cout << this << endl;
cout << _a << endl;
cout << _a << endl;
cout << _a << endl;
cout << _a << endl;
cout << _a << endl;
cout << _a << endl;
}
void Print(int x)
{
cout << this << endl;
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print(); // C、正常运行 仅去调用公共代码区的函数,并没有对空指针解引用
p->PrintA(); // B、运行崩溃,对空指针进行解引用
A aa;
aa.Print(1);
return 0;
}