C++类与对象学习笔记(一)
https://www.bilibili.com/video/BV1jm4y1w7pa?spm_id_from=333.788.player.switch&vd_source=e8984989cddeb3ef7b7e9fd89098dbe8&p=6
🚩🚩🚩来自b站“码农论坛”的视频“类与对象”做的笔记🚩🚩🚩
类与对象
- 1-1 cpp结构体的成员函数
- 1-2 对象和类-类的访问权限
- 1-3 对象和类-简单使用类
- 1-4 构造函数和析构函数
1-1 cpp结构体的成员函数
在 C 语言中,结构体不能包含函数。
C 语言的结构体只能用于存储数据,不能直接定义逻辑或操作。
尽管你可以将函数指针存储在结构体中,以间接实现某种形式的“行为”,但这不是将函数作为结构体的成员。
#include <stdio.h>
// 定义一个结构体
struct Point {
int x;
int y;
};
// 定义一个函数
void printPoint(struct Point p) {
printf("Point(%d, %d)\n", p.x, p.y);
}
int main() {
struct Point p1 = {10, 20};
printPoint(p1); // 使用函数
return 0;
}
在这个示例中,struct Point 只包含数据成员,其逻辑是通过外部函数来实现的。
//在 C++ 语言中,结构体(struct)可以包含成员函数。
//这使得结构体不仅可以存储数据,还可以对这些数据进行操作。
//C++ 的结构体和类(class)之间的主要区别只是默认的访问控制:结构体成员默认是公共的,而类成员默认是私有的。
#include <iostream>
using namespace std;
// 定义一个结构体
struct Point {
int x;
int y;
// 成员函数
void print() {
cout << "Point(" << x << ", " << y << ")" << endl;
}
};
int main() {
Point p1 = {10, 20};
p1.print(); // 调用成员函数
return 0;
}
在这个示例中,struct Point 包含一个成员函数 print(),该函数可以访问和操作结构体的成员 x 和 y。
结构体(struct)在C语言和C++中的其他差异
访问控制:C语言中的结构体没有访问控制的概念,所有成员默认都是公有的;而C++中的结构体可以有访问控制,默认情况下成员是公有的。继承:C语言不支持继承,因此结构体不能作为其他结构体或类型的基类;而C++支持继承,结构体可以作为基类。
构造函数和析构函数:C语言中没有构造函数和析构函数的概念;而C++中的结构体可以有构造函数和析构函数。
类型兼容性:C语言中的结构体类型严格依赖于其声明,即使成员列表相同,两个不同的结构体类型也不兼容;而C++中的结构体支持类型兼容性,如果两个结构体具有相同的成员列表,则它们是兼容的。
命名空间:C语言没有命名空间的概念,所有的结构体定义都在全局作用域内;而C++支持命名空间,允许将结构体定义组织在不同的命名空间中,以避免命名冲突。
//在CPP中,结构体就是类,把定义结构体的关键字改成class
#include <iostream>
using namespace std;
// 结构体变类:1.将struct改成class 2.将class里面加上public:
class Point {
public:
int x;
int y;
// 成员函数
void print() {
cout << "Point(" << x << ", " << y << ")" << endl;
}
};
int main() {
Point p1;
p1 = {10, 20};
p1.print(); // 调用成员函数
return 0;
}
//1.类的成员函数可以在类的外面,加类名和:: 2.在类里面只需要写函数的声明即可
class Point {
public:
int x;
int y;
// 成员函数
void print();
};
void Point::print() {
cout << "Point(" << x << ", " << y << ")" << endl;
}
int main() {
Point p1;
p1 = {10, 20};
p1.print(); // 调用成员函数
return 0;
}
1-2 对象和类-类的访问权限
- 类的成员有三种访问权限:public、private和protected,分别表示公有的、私有的和受保护的。
- 在类的内部(类的成员函数中),无论成员被声明为public还是private,都是可以访问。
- 在类的内部(定义类的代码之外),只能访问public成员,不能访问private,protected成员。
- 在一个类体的定义中,private和public可以出现多次。
- 结构体的成员缺省为public,类的成员缺省为private。eg.: 对类来说,所有的成员都是私有的,在main函数中无法直接访问
- private的意义在于隐藏类的数据和实现,把需要向外暴露的成员声明为public。
#include <iostream>
using namespace std;
class Point {
public:
int x;
int y;
void print();// 成员函数
};
void Point::print() {
cout << "Point(" << x << ", " << y << ", "<< z <<")" << endl;
}
int main() {
Point p1;
p1 = {10,20};
p1.print(); // 调用成员函数
return 0;
}
1-3 对象和类-简单使用类
编程思想和方法的改变,披着C++外衣的C程序员
1.类的成员函数可以直接访问该类其它的成员函数(可以递归)。
2.类的成员函数可以重载,可以使用默认参数。
3.类指针的用法与结构体指针用法相同。
4.类的成员可以是任意数据类型(类中枚举)。
5.可以为类的成员指定缺省值(C++11标准)。
6.类可以创建对象数组,就像结构体数组一样。
7.对象可以作为实参传递给函数,一般传引用。
8.可以用new动态创建对象,用delete释放对象。
9.在类的外部,一般不直接访问(读和写)对象的成员,而是用成员函数。
10.对象一般不用memset()清空成员变量,可以写一个专用于清空成员变量的成员函数。
11.对类和对象用sizeof运算符意义不大,一般不用。
12.用结构体描述纯粹的数据,用类描述对象。
13.在类的声明中定义的函数都将自动成为内联函数;在类的声明之外定义的函数如果使用了inline限定符,也是内联函数。
14.为了区分类的成员变量和成员函数的形参,把成员变量名加m_前缀或_后缀,如m_name或name_。
15.类的分文件编写和函数分文件编写的思路和方法是一样的,一般情况下,把声明类的代码放在头文件中,把成员函数定义的代码放在源文件中。
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
class Point {
public:
int x;
int y;
string name;
enum{girl = 1, boy = 2};
int sex;
int times = 0;//记录递归的次数
//9.在类的外部,一般不直接访问(读和写)对象的成员,而是用三个成员函数:清空,获取值,设置值
void initdata()//专用于清空全部成员变量的成员函数
{
name.clear();
x = 0;
}
int get_x()
{
return x;
}
void set_x(int x)
{
}
void Show()
{
if(times++ > 10) return;
cout << x << " " << y << endl;
if(sex == girl)
{
;
}
}
void print();//这里要提前声明,不然会报错
};
inline void Point::print()
{
cout << x << y << endl;
}
int main()
{
Point p1;
p1 = {10,20};
p1.Show(); // 调用成员函数
p1.sex = p1.girl;
return 0;
}
1-4 构造函数和析构函数
构造函数:在创建对象时,“自动”地进行初始化工作。
析构函数:在销毁对象前,“自动”地完成清理工作。
1.构造函数: 语法: 类名(){…} ·访问权限必须是public ·函数名必须与类名相同 ·没有返回值,也不写void ·可以有参数,可以重载,可以有默认参数 ·创建对象时会自动调用一次,不能手工调用。
2.析构函数:用于释放资源 语法:~类名(){…} ·访问权限必须是public。 ·函数名必须在类名前加~ ·没有返回值,不能重载 ·销毁对象前只会自动调用一次,但是可以手工调用,不过手工调用的场景非常少
demo程序
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
class Point{
public:
string m_name; //姓名属性
int m_age;//年龄属性
char m_memo[301];//备注
int* m_ptr;
Point()//构造函数,对三个成员变量进行初始化
{
initdata();
//m_ptr = nullptr;
cout << "调用了Point()构造函数。\n" << endl;
}
Point(string name)//构造函数,对三个成员变量进行初始化
{
initdata();
cout << "调用了Point(name)构造函数。 \n" << endl;
}
Point(string name, int age)//构造函数,对三个成员变量进行初始化
{
initdata();
cout << "调用了Point(name,age)构造函数。 \n" << endl;
}
void initdata()
{
m_name.clear(); m_age = 0; memset(m_memo,0,sizeof(m_memo));
}
~Point()
{
//if(m_ptr != nullptr) delete m_ptr; //这里如果构造函数没有被调用,指针不会初始化为空
cout << "调用了~Point()析构函数。\n" << endl;
}
void Show()
{
cout << "姓名:" << m_name << ",年龄:" << m_age <<",备注:" << m_memo << endl;
}
};
int main()
{
//Point p1;//创建对象并自动调用没有参数的构造函数
//Point p1("小红");//调用有name参数的构造函数,多个构造函数不能同时调用哦
Point p1("小红",50);//调用有name,age参数的构造函数
p1.Show(); //调用成员函数
p1.~Point();
return 0;
}
析构函数的细节:
注意:
1.如果没有提供构造/析构函数,编译器将提供空实现的构造/析构函数
2.如果提供了构造/析构函数,编译器将不提供空实现的构造/析构函数
//输入输出都没有的就是空实现的构造/析构函数,如果自己定义了有参数的构造函数,那么在主函数调用时就不能调用空输入参数的函数了。
3.创建对象时,如果重载了构造函数,编译器根据实参匹配相应的构造函数4.创建对象时不要在对象名后面加空的圆括号,编译器误认为时声明函数(没有构造函数、构造函数没有参数、构造函数的参数都有默认参数)
5.在构造函数名后面加括号和参数不是调用构造函数,是创建匿名对象(!!!很重要!!!)
6.接受一个参数的构造函数允许使用赋值语法将对象初始化为一个值(可能会导致问题,不推荐使用)7.以下两行代码有本质的区别: Point p1 = Point("cy"20);//显示创建对象 Point p1;//创建对象 p1 = Point("cy"20);//创建匿名对象,然后给现有的对象赋值;一共创建了两个对象
8.用new/delete创建/销毁对象时,也会调用构造/析构函数
9.不建议在构造/析构函数中写太多的代码,可以调用成员函数10.除了初始化,不建议让构造做太多工作(只能成功不会失败),因为没有返回值
11.C++11支持使用统一初始化列表
12.如果类的成员也是类,创建对象的时候,先构造成员类;销毁对象的时候,先析构成员类
int main()
{
//Point* p1 = new Point();
Point* p1 = new Point("cy",20);
p1->Show(); // 调用成员函数
delete p1;
return 0;
}