c++构造和析构

简介: c++构造和析构

2053226834c04b0aa8d79e2732edbaa3.png

目录

1.构造函数

1.构造函数特性

构造函数名字和类名相同

构造函数没有返回值(void有返回值,返回值为空)

不写构造函数,每一个类中都存在默认的构造函数,默认的构造函数是没有参数的

default显示使用默认的构造函数

delete删掉默认函数

当我们自己写了构造函数,默认的构造函数就不存在

构造函数是不需要自己调用,在构造函数对象的时候自己调用

构造函数决定了对象的长相

无参构造函数可以构造无参对象

有参构造函数,对象必须要带有参数

构造函数允许被重载和缺省

构造函数一般情况是公有属性

构造函数一般是用来给数据初始化

构造函数允许调用另一个构造函数,但是必须采用初始化参数列表的写法:

构造函数的初始化参数列表: 构造函数名(参数1,参数2,…):成员1(参数1),成员2(参数2),…{}

2.综合代码

#include<iostream>

#include<string>

using namespace std;

class MM

{

public:

//构造函数

//MM()=default;          //使用的是默认的构造函数

//MM()=delete;

MM()

{

 cout<<"无参构造函数"<<endl;

}

MM(int a)

{

 cout<<"具有一个参数的构造函数"<<endl;

}

protected:

};

class Girl

{

public:

Girl()=delete;

protected:

};

class Student

{

public:

Student(){m_age=15,m_name="k";}

Student(string name,int age)

{

 //做初始化操作

 m_name=name;

 m_age=age;

}

void printStudent()

{

 cout<<m_name<<"\t"<<m_age<<endl;

}

protected:

string m_name;

int m_age;

};

class Test{

public:

//构造函数特殊写法

Test(int a,int b):a(a),b(b){}

Test():Test(9,8){}             //无参构造函数调用有参构造函数,构造委托

//Test()=default;

void print()

{

 cout<<a<<"\t"<<b<<endl;

}

protected:

int a=0;

int b=0;

};

struct Data

{

int a;

int b;

int c;

Data(int a):a(a){}

Data(int a,int b,int c):a(a),b(b),c(c){

 cout<<"调用三个参数的构造函数"<<endl;

}

void print(){

 cout<<a<<"\t"<<b<<"\t"<<c<<endl;

}

};

int main()

{

MM boy;

MM girl(1);

 

//Girl girl;    //默认构造函数已经删除,且自己没写构造函数,所以错误

 

//普通对象

Student mm("zhangkai",15);

mm.printStudent();

 

//new一个对象,new的过程是先在自由存储区创建一个无名对象,再把地址返回

Student* pstu = new Student("zhi",29);  

pstu->printStudent();

 

//对象数组

Student stuarry[3]; //无名对象,需要无参构造函数,否则错误

stuarry[1].printStudent();

stuarry[2].printStudent();

stuarry[0].printStudent();

 

//初始化参数列表

Test test(99,88);

test.print();

Test bb;

bb.print();

Test xx={88,99};  //这个过程也是调用构造函数过程,{}中数据个数要和构造函数参数一致

xx.print();

 

Data oo(3);

oo.print();

Data data(1,2,3);

data.print();

}

2.析构函数

1.析构函数特性

函数名等于~加上类名

析构函数没有参数,所以析构函数不能被重载也不能被缺省

对象死亡(生命周期结束)的最后一个事情是调用析构函数

析构函数都是公有属性

什么时候写析构函数?

当类的成员new了内存就需要自己手动写析构函数

不写析构函数,也会存在一个析构函数,但是不具有释放new的内存的功能

2.综合代码

#include<iostream>

using namespace std;

class MM

{

public:

MM()

{

 p=new int;

}

void freeMM()

{

 delete p;

 p=nullptr;

}

~MM()

{

 cout<<"我是析构函数"<<endl;

 delete p;

 p=nullptr;

}

protected:

int* p;

};

int main()

{

{

 MM mm;

 //mm.freeMM();        //当然也可以自己写函数释放,不过要手动释放

 MM* p=new MM;

 delete p;            //立刻马上调用析构函数    

}

cout<<"..............."<<endl;

   return 0;

}

3.拷贝构造函数

1.拷贝构造函数特性

  1. 不写拷贝构造函数,存在一个默认拷贝构造函数
  2. 拷贝构造函数名和构造函数一样,算是构造函数特殊形态
  3. 拷贝构造函数唯一的一个参数就是对对象的引用
  • 普通引用
  • const引用
  • 右值引用——>移动拷贝
  1. 当我们通过一个对象产生另一个对象时候就会调用拷贝构造函数

2.综合代码

#include<iostream>

#include<string>

using namespace std;

class MM{

public:

MM()=default;

MM(MM& object)

{

 cout<<"调用拷贝构造函数"<<endl;

}

protected:

};

class Girl{

public:

Girl(string name,int age):name(name),age(age){}

Girl():Girl("",0){}

Girl(const Girl& object)

{  

 //拷贝构造函数就是通过一个对象赋值另一个对象

 name=object.name;

 age=object.age;

 cout<<"调用拷贝构造函数"<<endl;

}

void print()

{

 cout<<name<<"\t"<<age<<endl;

}

protected:

string name;

int age;

};

void printBoy(Girl girl)           //调用拷贝构造函数,Gilr girl=实参

{

girl.print();

}

void printGirl(Girl& girl)

{

girl.print();

}

void testGirl()

{

Girl mm("小妹",19);

Girl girl(mm);          //调用

girl.print();

Girl beauty=mm;           //调用

beauty.print();

cout<<"传入普通变量"<<endl;

printBoy(girl);                //调用

cout<<"传入引用"<<endl;        

//不调用拷贝构造函数

printGirl(girl);

 

//匿名对象的拷贝构造函数,匿名对象是右值,可以用const或者右值引用

//const里不可修改

//右值引用里提供了可修改的接口

Girl xx=Girl("zhangkai",19);

xx.print();

}

class Boy{

public:

Boy(string name,int age):name(name),age(age){}

Boy(Boy&& object)

{

 name=object.name;

 age=object.age;

 cout<<"右值引用的拷贝构造"<<endl;

}

Boy(Boy& object)

{

 name=object.name;

 age=object.age;

 cout<<"普通的拷贝构造"<<endl;

}

protected:

string name;

int age;

};

void testBoy(){

Boy boy("boy",10);          

Boy bb=boy;                   //调用普通对象

Boy coolman=Boy("dasd",29);   //右值引用的拷贝构造函数

//没有打印结果,IDE做了优化,看不到

}

int main()

{

MM mm;

MM girl=mm;               //会调用拷贝构造函数

MM boy(girl);             //会调用拷贝构造函数

//string str="dasd";

//string str2(str);

//string str3=str2;

//string类型赋值实际上是调用拷贝构造函数

 

//调用拷贝构造函数语句一定有类名

//不调用拷贝构造函数,这是先创建对象,然后赋值,属于运算符重载

MM npc;                    

npc=girl;  

 

cout<<"............"<<endl;  

testGirl();

cout<<"............"<<endl;

testBoy();

return 0;

}

3.深浅拷贝问题

深浅拷贝只在类中存在指针,并且做了内存申请的,才会存在引发析构问题(内存释放问题)

  • 默认的拷贝构造都是浅拷贝
  • 拷贝构造函数中做普通的赋值操作也是浅拷贝
错误代码

#include<iostream>

#include<cstring>

using namespace std;

class MM{

public:

MM(const char* str,int num)

{

 int length=strlen(str)+1;

 name=new char[length];

 strcpy_s(name,length,str);

 age=num;

}

~MM()

{

 if(name!=nullptr)

 {

  delete[] name;

  name=nullptr;

 }

}

protected:

char* name;

int age;

};

void testQuestion(){

MM mm("zhangzhang",19);

MM xx=mm;

}

int main()  

{

testQuestion();

return 0;

}

原因如下图:

7810214a5d9c4a5e85ace54c3f24456d.png

正确代码

#include<iostream>

#include<cstring>

using namespace std;

class MM {

public:

MM(const char* str, int num)

{

 int length = strlen(str) + 1;

 name = new char[length];

 strcpy_s(name, length, str);

 age = num;

}

MM(const MM& object)

{

 //深拷贝

 int length = strlen(object.name) + 1;

 name = new char[length];

 strcpy_s(name, length, object.name);

 age = object.age;

}

~MM()

{

 if (name != nullptr)

 {

  delete[] name;

  name = nullptr;

 }

}

protected:

char* name;

int age;

};

void testQuestion() {

MM mm("zhangzhang", 19);

MM xx = mm;

}

int main()

{

testQuestion();

return 0;

}

解释:

4.构造和析构的顺序问题51813ea093a148969e8b98815f4349eb.png

  1. 一般情况构造顺序和析构顺序是相反的(先构造后释放,后构造先释放)
  2. new对象,调用delete直接被释放
  3. static对象,最后释放(生命周期最长)

#include<iostream>

#include<string>

using namespace std;

class MM

{

public:

MM(string info="A"):info(info){cout<<info;}

~MM(){cout<<info;}

protected:

string info;

};

void testOrder()

{

MM mm1("B");

static MM mm2("C");

MM* p=new MM("D");

delete p;

MM arr[3];

}

int main()

{

testOrder();

return 0;

}

5.c++类的组合

介绍:

一个类包含另一个类的对象为数据成员叫做类的组合。当多种事物是一个事物的一部分,采用组合类来完成描述,c++中组合的使用优先于继承

注意:类不能包含自身对象,否则会形成死循环

组合类的构造函数,必须要采用初始化参数列表的方式调用分支类的构造函数

组合类的构造顺序:先构造分支类,分支类的顺序只和声明顺序有关,和初始化参数列表一点毛线关系

#include<iostream>

#include<string>

using namespace std;

class MM

{

public:

MM(){cout<<"构造mm"<<endl;}

MM(string name):name(name){}

void print(){cout<<"MM:"<<name<<endl;}

protected:

string name;

};

class GG

{

public:

GG(){cout<<"构造gg"<<endl;}

GG(string name,int age):name(name),age(age){}

void print(){cout<<name<<"\t"<<age<<endl;}

protected:

string name;

int age;

};

class Family

{

public:

Family(string mmName,string ggName,int age):mm(mmName),gg(ggName,age){}

//但是分支类中必须存在无参的构造函数

Family(){cout<<"构造组合类"<<endl;}

void print(){gg.print();mm.print();}

protected:

MM mm;

GG gg;

};

int main()

{

Family dd("mm","gg",19);

dd.print();

Family object;

object.print();    //先分支再组合,且分支的顺序与声明的顺序一致

return 0;

}

6.c++类中类

  • 类中类的访问问题以及类中类先申明后定义的写法
  • 类中类依旧受权限限定
  • 访问必须要类名::剥洋葱的方式访问

#include<iostream>

using namespace std;

class xx {

public:

xx(){cout<<"外面的构造函数"<<endl;}

protected:

public:

//类中类依旧受权限限定,就相当于把一个类丢到另外一个类中,他们两个没有关系

class dd

{

public:

 dd()

 {

  cout<<"类中类构造函数"<<endl;

 }

 void print(){cout<<"类中类构造函数"<<endl;}

protected:

};

};

void testlzl()

{

xx::dd bb;

bb.print();

}

int main()

{

testlzl();

return 0;

}

版权声明:本文为CSDN博主「热爱编程的小K」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_72157449/article/details/128515945

相关文章
|
2月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
63 1
|
7月前
|
设计模式 编译器 C++
C++中的构造方法和析构方法详解
C++中的构造方法和析构方法详解
49 0
|
4月前
|
JavaScript Java C语言
面向对象编程(C++篇3)——析构
面向对象编程(C++篇3)——析构
32 2
|
4月前
|
JavaScript 前端开发 Java
面向对象编程(C++篇2)——构造
面向对象编程(C++篇2)——构造
33 0
|
7月前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
42 1
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
|
6月前
|
C++ 容器
C++之deque容器(构造、赋值、大小、插入与删除、存取、排序)
C++之deque容器(构造、赋值、大小、插入与删除、存取、排序)
|
6月前
|
C++ 容器
C++字符串string容器(构造、赋值、拼接、查找、替换、比较、存取、插入、删除、子串)
C++字符串string容器(构造、赋值、拼接、查找、替换、比较、存取、插入、删除、子串)
|
7月前
|
存储 安全 C语言
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(上)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
38 2
|
7月前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(下)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
45 1
|
6月前
|
算法 C++ 容器
C++之vector容器操作(构造、赋值、扩容、插入、删除、交换、预留空间、遍历)
C++之vector容器操作(构造、赋值、扩容、插入、删除、交换、预留空间、遍历)
288 0