#创作计划# C++中的类与结构 1
2025-08-20 20:42:37
发布于:广东
Markdown源码4542字,渲染后实际3433字
最近几天被dp和模拟打傻了,有错误请指出谢谢
前言
感谢各位留步!
本文章介绍了C++中类和结构体的定义,以及类的构造/析构/复制构造函数。
广告
结构
定义方法
struct A{
//在这里定义结构的成员变量和函数
};
在花括号内,可以使用任何有效的方法定义成员变量及函数(不常见)。
同时,可以在右花括号后、分号前的位置定义变量,如
struct Book{
int id;
string author;
string title;
}book;//等于Book book
struct Student{
int id;
string name;
int age;
float height,weight;
}class[40];//等于Student class[40]
同时,也可以使用匿名结构体。
struct{
int idx,num;
}a[100010];
访问成员变量/函数的方法
#include<bits/stdc++.h>
using namespace std;
struct A{
int id;
void print(){
cout << id << endl;
}
};
int main(){
A a;//结构体对象
a.id=12;//使用.运算符操作成员变量
a.print();//使用.运算符调用成员函数
A *p = new A;//指向结构体对象的指针
p->id = 12;//使用->运算符,使用方法与.运算符相同
p->print();
}
运行结果:
结构体的特点
- 结构体的默认访问权限为
public
。
由于这个特点,结构体的语法相对于类来说更加简洁。 - 结构体一般使用花括号的方式初始化。
由于在C++中主要使用类,这里就不再仔细讲解结构体。想要继续深入了解的可以参考这里。
类
定义方法
class A{
//在这里放置你的代码
};
与结构体相同,类也可以像
class A{
//在这里放置你的代码
}a;
这样定义变量。同样,类也可以是匿名的。
#include<bits/stdc++.h>
using namespace std;
class {
int i;
}a;
int main(){
}
实测编译通过。
类的构造/析构函数
构造函数
现在有一个类A,让我们试着像结构体一样声明并初始化一个A
的对象a
:
#include<bits/stdc++.h>
using namespace std;
class A{
int i;
};
int main(){
A a;
a.i = 12;
}
然后,报错了。
此处的报错告诉我们,类A
中的变量i
是一个私有成员变量。
这就是结构体与类的第一个不同点:
结构体成员默认公有
public
,类成员默认私有public
。
想这样访问a.i
,有一种简单的方法:
class A{
public://通过访问修饰符public声明i是公有成员变量
int i;
};
但是,这样就无法发挥出类的封装优势了。其实,我们可以定义一个构造函数来初始化类。
#include<bits/stdc++.h>
using namespace std;
class A{
int i;
public://注意构造函数必须为public
A(int _i):i(_i){
cout << "i:" << i << endl;
}
};
int main(){
A a(3);
}
运行结果:
构造函数的名称只能是类名,并且不带返回值。
此处的i(_i)
是初始化列表,可以简化构造函数。
构造函数中也可以写一些复杂的功能,例如初始化指针:
#include<bits/stdc++.h>
using namespace std;
class A{
int i;
int *p;//指针
public://注意构造函数必须为public
A(int _i):i(_i){
p = new int(i);
cout << "i:" << i << endl;
cout << "p:" << p << endl;
}
};
int main(){
A a(3);
}
运行结果:
析构函数
众所周知,一个new
出来的指针不delete
是很危险的。
但是,直接delete p
显然不可能,因为p
是类A
的私有成员变量。
这个时候,我们可以定义一个析构函数来释放指针,这个函数会在对象生命周期结束时自动调用。
#include<bits/stdc++.h>
using namespace std;
class A{
int i;
int *p;//指针
public://注意构造函数和析构函数都必须为public
A(int _i):i(_i){
p = new int(i);
cout << "i:" << i << endl;
cout << "p:" << p << endl;
}
~A(){
cout << "析构函数调用";
delete p;
}
};
int main(){
A a(3);
}
运行结果:
析构函数同样不能有任何返回值,并且格式固定波浪号后接类名不带参数。
这里的析构函数在主函数结束时自动调用。
拷贝构造函数
假设有一个对象a
,现在要将a
复制到b
,这时候就需要拷贝构造函数。
为了
水字数后续讲解,这里的类简化掉了指针p
。
#include<bits/stdc++.h>
using namespace std;
class A{
int i;
public://注意构造函数和析构函数都必须为public
A(int _i):i(_i){
cout << "构造函数调用" << i << endl;
}
~A(){
cout << "析构函数调用" << i << endl;
}
A(const A & other){
i = other.i;
cout << "复制构造函数调用" << i << endl;
}
};
int main(){
A a(3);
A b(a);//调用了复制构造函数
}
运行结果:
好的!让我们回收之前的伏笔,给类A增加一个成员函数int * p
。
#include<bits/stdc++.h>
using namespace std;
class A{
int i;
int * p;
public://注意构造函数和析构函数都必须为public
A(int _i):i(_i){
cout << "构造函数调用" << i << endl;
p = new int(_i);
}
~A(){
cout << "析构函数调用" << i << endl;
delete p;
}
A(const A & other){
i = other.i;
p=other.p;
cout << "复制构造函数调用" << i << endl;
}
};
int main(){
A a(3);
A b(a);//调用了复制构造函数
}
运行——
让我们在权威网站查找一下这个返回值
那么,这里的错误原因是什么呢?
注意到在拷贝构造函数中,直接将other.p
赋值给了p
。
让我们模拟程序的最后几步:
- 程序首先执行其中一个对象的析构函数,
delete
了这个对象中p
指向的地址。 - 程序执行另一个对象的析构函数。欸,问题就出在这里:
由于两个对象的p都指向一个地址,所以......
后析构的对象的析构函数delete
了一个已经被释放的地址!
这种情况被称为浅拷贝。那么我们如何解决这种情况呢?
只需要让复制构造函数只复制other.p
指向的值。
将拷贝构造函数改编为:
A(const A & other){
i = other.i;
p=new int(*(other.p));//只复制值
cout << "复制构造函数调用" << i << endl;
}
运行结果:
程序正常退出。
结语
这篇讲解到此结束,下期我会讲解更多关于类的知识,包括友元,继承等。
我不行了再写就要残废了
参考资料
https://www.runoob.com/cplusplus/cpp-struct.html
https://www.runoob.com/cplusplus/cpp-classes-objects.html
https://cn.bing.com
后记
哇啊啊啊啊啊!好!恐!怖!!!(发出尖锐爆鸣)
全部评论 3
认真,给赞
21小时前 来自 广东
0ddd
昨天 来自 浙江
0咕咕嘎嘎!
昨天 来自 广东
0爹,给点建议
昨天 来自 广东
0别叫爹啊
昨天 来自 浙江
0
@@AC君求精选,集
中训营写的但求稍微通融,有错误请及时指正!昨天 来自 广东
0不要逼我喊爹(虽然其实我现在处于随地大小爹)昨天 来自 广东
0
有帮助,赞一个