C/C++基础之二(类的构造函数)
Talk is cheap. Show me the code.
1. 构造函数,有参,无参?
2. 析构函数的调用时机?
3. 如何在构造函数中初始化某个成员类?
4. 拷贝构造函数的凶残?
5. 操作符重载的奇妙设计!
#include <iostream>
#include <string.h>
using namespace std;
class Person
{
private:
char *name;
char *work;
char *sex;
int age;
public:
Person();
Person(char *name,char *work,int age,char *sex=(char *)"f");
~Person();
void setAge(int age);
void setName(char *name);
void printInfo();
};
//无参构造函数
Person::Person(/* args */)
{
cout << "Person()" << endl;
// 对指针变量赋初值很重要,否则是野指针,直接访问非常危险,会有问题的
this->name = NULL;
this->work = NULL;
this->sex = NULL;
}
Person::Person(char *name,char *work,int age,char *sex)
{
// 下面变量共享内存的写法,也叫做浅拷贝,如果外部被释放,那么这里就成了野指针。很危险
// this->name = name;
// this->work = work;
// 分配内存,也叫做深拷贝,安全,但要记得自己释放new出来的内存,释放交给析构函数
this->name = new char[strlen(name)+1];
strcpy(this->name,name);
this->work = new char[strlen(work)+1];
strcpy(this->work,work);
this->sex = new char[strlen(sex)+1];
strcpy(this->sex,sex);
this->age = age;
cout << "Person(char *name,char *work,int age)" << ",name:"<< name << ",work:"<< work <<",age:"<< age <<",sex:"<< sex << endl;
}
void Person::printInfo()
{
cout << "name:"<< name << ",work:"<< work <<",age:"<< age << endl;
}
// 析构函数
Person::~Person()
{
cout << "~Person(),name:" << this->name << endl;
if (this->name)
{
cout << "delete name:" << this->name << endl;
// C++ 要严格遵守 new ↔ delete,new[] ↔ delete[]。
// delete this->name;
delete[] this->name;
}
if (this->work)
{
cout << "delete work:" << this->work << endl;
// C++ 要严格遵守 new ↔ delete,new[] ↔ delete[]。
delete[] this->work;
}
if(this->sex)
{
// C++ 要严格遵守 new ↔ delete,new[] ↔ delete[]。
cout << "delete sex:" << this->sex << endl;
delete[] this->sex;
}
}
class Student
{
private:
Person father;
Person mother;
int id;
public:
Student()
{
cout << "Student()" << endl;
}
// 构造函数中完成成员变量的初始化,成员变量的初始化和:father(...),mother(...)无关,之和上面的成员定义顺序有关
Student(char *fatherName,char *motherName,int id):father(fatherName,"CFO",28,(char *)"M"),mother(motherName,"BOSS",18)
{
this->id = id;
}
~Student()
{
cout << "~Student()"<<endl;
}
};
void person_test()
{
Person *p = new Person("lisi","student",18);
//new出来的内存一定要释放掉,否则它是不会释放的
delete p;
}
int main()
{
Person per;
//注意下面这行代码并没有创建per1对象,只是一个方法声明,类似 int sum();
Person per1();
// 调用有参数的构造函数,最后一个sex参数使用默认值
Person per2("zhangsan","laywer",48);
Person per3("wangwu","CEO",29,"M");
person_test();
Student s("Jack","Alice",10);
return 0;
}
- 输出如下
Person()
Person(char *name,char *work,int age),name:zhangsan,work:laywer,age:48,sex:f
Person(char *name,char *work,int age),name:wangwu,work:CEO,age:29,sex:M
Person(char *name,char *work,int age),name:lisi,work:student,age:18,sex:f
~Person(),name:lisi
delete name:lisi
delete work:student
delete sex:f
Person(char *name,char *work,int age),name:Jack,work:CFO,age:28,sex:M
Person(char *name,char *work,int age),name:Alice,work:BOSS,age:18,sex:f
~Student()
~Person(),name:Alice
delete name:Alice
delete work:BOSS
delete sex:f
~Person(),name:Jack
delete name:Jack
delete work:CFO
delete sex:M
~Person(),name:wangwu
delete name:wangwu
delete work:CEO
delete sex:M
~Person(),name:zhangsan
delete name:zhangsan
delete work:laywer
delete sex:f
~Person(),name:
我们写了一些属性以及get/set方法,当这个类交给一个工具类的时候他想做一些操作,因为属性一般是private的,因此他必须要调用get,set方法,那么就出现了一种叫做firend的函数,可以直接访问私有属性,上代码:
#include <iostream>
using namespace std;
class Point;
class PointUtils
{
private:
/* data */
public:
PointUtils
(/* args */);
Point pointSum(const Point& p1,const Point& p2);
~PointUtils
();
};
class Point{
private:
int x;
int y;
public:
Point(){}
Point(int x,int y){
this->x = x;
this->y = y;
}
void setX(int x){
this->x = x;
}
void setY(int y){
this->y = y;
}
int getX(){
return this->x;
}
int getY(){
return this->y;
}
//声明PointUtils的pointSum是我的朋友,可以访问我的属性
friend Point PointUtils::pointSum(const Point& p1,const Point& p2);
void printInfo(){
cout << "x:" << this->x << ",y:" << this->y << endl;
}
};
PointUtils::PointUtils(/* args */)
{
}
Point PointUtils::pointSum(const Point& p1,const Point& p2){
Point result;
//友元函数,可以直接访问x,y,就不用使用get方法啦
result.x = p1.x + p2.x;
result.y = p1.y + p2.y;
return result;
}
PointUtils::~PointUtils()
{
}
int main(){
Point p1(10,20);
Point p2(20,30);
PointUtils util;
Point result = util.pointSum(p1,p2);
result.printInfo();
return 0;
}
然后上面的程序似乎没有问题,但是其实是有问题的。这个问题来自于类的拷贝:
1️⃣ 浅拷贝的定义
浅拷贝就是 直接拷贝对象的所有成员,包括指针 只拷贝指针本身的值(地址),而不拷贝指针指向的内容。
例如:
class Demo {
public:
int* ptr;
Demo(int val) { ptr = new int(val); }
~Demo() { delete ptr; }
};
int main() {
Demo d1(5);
Demo d2 = d1; // 默认拷贝构造函数执行 → 浅拷贝
}
d2.ptr 和 d1.ptr 指向同一块堆内存
当 d1 析构时释放了内存 → d2.ptr 悬空
再析构 d2 → double free / 崩溃
✅ 这就是浅拷贝的典型问题。
2️⃣ 什么时候会触发浅拷贝
(1) 按值传递对象
void foo(Demo d) { … } // d 是按值传递
Demo obj(5);
foo(obj); // 触发拷贝构造(默认浅拷贝)
函数内部会调用 拷贝构造函数
默认拷贝构造函数就是浅拷贝
(2) 返回对象(按值返回)
Demo createDemo() {
Demo d(10);
return d; // 触发拷贝构造(默认浅拷贝)
}
编译器通常会做 返回值优化(RVO) 避免拷贝,但如果没有 RVO,也会发生浅拷贝
(3) 对象赋值
Demo d1(5);
Demo d2(10);
d2 = d1; // 默认赋值运算符 → 浅拷贝
这里 d2 的原 ptr 被覆盖
如果原 ptr 已经分配了内存,可能造成内存泄漏
同时 d1 和 d2 共享同一块内存 → double-free
3️⃣ 浅拷贝的问题
悬空指针:多个对象指向同一块动态分配内存
double-free:析构函数释放同一块内存两次
内存泄漏:赋值时忘记释放旧的资源
4️⃣ 解决方法
深拷贝(deep copy):在拷贝构造和赋值运算符中,重新分配内存并复制内容
使用智能指针或 std::string:自动管理内存,避免浅拷贝问题
下面看看我们的例子,使用深拷贝构造函数&‘=’运算符重写,解决这个棘手的问题:
#include <iostream>
#include <string.h>
using namespace std;
class Point;
class PointUtils
{
private:
/* data */
public:
PointUtils
(/* args */);
Point pointSum(const Point& p1,const Point& p2);
~PointUtils
();
};
class Point{
private:
int x;
int y;
char *niceName;
public:
Point(){
niceName = NULL;
cout << "Point()" << endl;
}
// 深拷贝构造函数
Point(const Point& p){
cout << "Copy (const Point& p)" << endl;
this->x = p.x;
this->y = p.y;
if(p.niceName){
this->niceName = new char[strlen(p.niceName)+1];
strcpy(this->niceName,p.niceName);
}else{
this->niceName = nullptr;
}
}
// 赋值运算符
Point& operator=(const Point& other){
cout << "operator=" << endl;
if(this == &other) return *this;
x = other.x;
y = other.y;
delete[] niceName;
if(other.niceName){
niceName = new char[strlen(other.niceName)+1];
strcpy(niceName, other.niceName);
} else niceName = nullptr;
return *this;
}
Point(int x,int y,const char *niceName){
cout << "Point(int x,int y)" << "x:" << x <<",y:" << y << niceName << endl;
this->x = x;
this->y = y;
this->niceName = new char[strlen(niceName)+1];
strcpy(this->niceName,niceName);
}
void setX(int x){
this->x = x;
}
void setY(int y){
this->y = y;
}
int getX(){
return this->x;
}
int getY(){
return this->y;
}
friend Point PointUtils::pointSum(const Point& p1,const Point& p2);
// 重载 int + point
friend Point operator+(int a, Point& p){
cout << "operator+(int a, Point& p)" << endl;
Point result;
result.x = a + p.x;
result.y = a + p.y;
result.niceName = new char[10];
strcpy(result.niceName,"operator+");
return result;
}
//重载 point + int
friend Point operator+(Point& p, int a){
cout << "operator+(Point& p, int a)" << endl;
Point result;
result.x = a + p.x;
result.y = a + p.y;
result.niceName = new char[10];
strcpy(result.niceName,"operator+");
// result.niceName = nullptr;
return result;
}
// 重载 前++
Point& operator++(){
cout << "operator++()" << endl;
this->x = this->x + 1;
this->y = this->y + 1;
if (niceName)
{
delete[] niceName;
}
niceName = new char[10+1];
strcpy(niceName,"operator++");
return *this;
}
Point operator++(int){
Point temp(*this); // 保存当前值
x++; y++;
return temp; // 返回修改前的副本
}
void printInfo(){
cout << "printInfo" << "x:" << this->x << ",y:" << this->y;
if (niceName)
{
cout << niceName;
}
cout << endl;
}
~Point(){
cout << "~Point()" << "x:" << this->x << ",y:" << this->y;
if (niceName)
{
cout << "delete:" << niceName;
// delete只能删除 new 出来的,niceName = "abcd" 就不能删除哦!否则要出大问题
delete[] niceName;
}
cout << endl;
// cout << this << end;
}
};
PointUtils::PointUtils(/* args */)
{
}
Point PointUtils::pointSum(const Point& p1,const Point& p2){
Point result;
result.x = p1.x + p2.x;
result.y = p1.y + p2.y;
// 判断 p1.niceName 和 p2.niceName 是否为空
const char* name1 = p1.niceName ? p1.niceName : "";
const char* name2 = p2.niceName ? p2.niceName : "";
// 分配内存:长度 = name1 + name2 + 1('\0')
size_t len = strlen(name1) + strlen(name2) + 1;
result.niceName = new char[len];
// 拼接字符串
strcpy(result.niceName, name1);
strcat(result.niceName, name2);
return result;
}
PointUtils::~PointUtils()
{
}
int main(){
// Point p1(10,20);
// Point p2(20,30);
// PointUtils util;
// Point result = util.pointSum(p1,p2);
// Point result = p1.add(p2);
int ax[] = {1,2};
int ay[] = {1,2};
int bx[] = {3,4};
int by[] = {3,4};
PointUtils util;
Point pArray[2];
for(int i=0;i<2;i++){
cout << "For" << endl;
char* aNiceName = (char*)malloc(sizeof(char)*2+1);
sprintf(aNiceName, "a%d", i);
char* bNiceName = (char*)malloc(sizeof(char)*2+1);
sprintf(bNiceName, "b%d", i);
Point p1(ax[i],ay[i],aNiceName);
Point p2(bx[i],by[i],bNiceName);
Point result = util.pointSum(p1,p2);
result.printInfo();
// operator= 对于‘=’的重载生效,深拷贝!
pArray[i] = result;
free(aNiceName);
free(bNiceName);
}
cout << "***last dance***" << endl;
Point result2 = 3 + pArray[0];
Point result3 = pArray[1] + 3;
result2.printInfo();
result3.printInfo();
// 运算符重载,前++
++result3;
result3.printInfo();
result2++;
result2.printInfo();
return 0;
}
输出结果
Point()
Point()
For
Point(int x,int y)x:1,y:1a0
Point(int x,int y)x:3,y:3b0
Point()
printInfox:4,y:4a0b0
operator=
~Point()x:4,y:4delete:a0b0
~Point()x:3,y:3delete:b0
~Point()x:1,y:1delete:a0
For
Point(int x,int y)x:2,y:2a1
Point(int x,int y)x:4,y:4b1
Point()
printInfox:6,y:6a1b1
operator=
~Point()x:6,y:6delete:a1b1
~Point()x:4,y:4delete:b1
~Point()x:2,y:2delete:a1
***last dance***
operator+(int a, Point& p)
Point()
operator+(Point& p, int a)
Point()
printInfox:7,y:7operator+
printInfox:9,y:9operator+
operator++()
printInfox:10,y:10operator++
Copy (const Point& p)
not null?operator+~Point()x:7,y:7delete:operator+
printInfox:8,y:8operator+
~Point()x:10,y:10delete:operator++
~Point()x:8,y:8delete:operator+
~Point()x:6,y:6delete:a1b1
~Point()x:4,y:4delete:a0b0