专门做老年旅游的网站,南宁建筑网站,网站开发效率,重庆网站优化排名软件方案本专栏目的
更新C/C的基础语法#xff0c;包括C的一些新特性
前言 周末休息了#xff0c;没有更新#xff0c;请大家见谅哈#xff1b;构造函数、析构函数可以说便随着C每一个程序#xff0c;故学构造函数、析构函数是必要的#xff1b;C语言后面也会继续更新知识点的基础语法包括C的一些新特性
前言 周末休息了没有更新请大家见谅哈构造函数、析构函数可以说便随着C每一个程序故学构造函数、析构函数是必要的C语言后面也会继续更新知识点如内联汇编本人现在正在写一个C语言的图书管理系统1000多行代码包含之前所学的所有知识点包括链表和顺序表等数据结构请大家耐心等待预计国庆前写完更新。 文章目录 构造函数析构函数构造/析构函数调用机制析构函数调用时间 构造/析构函数用途展示构造函数分类无参构造函数有参构造函数拷贝构造函数(赋值构造)移动构造函数 深拷贝和浅拷贝构造函数的初始化参数列表初始化参数列表类中类如何构造 构造函数
首先我们写一个学生类定义一个公有函数用来打印学生信息
#include iostreamclass Student
{
public:void print() {std::cout 学号: m_uid 姓名: m_name 年龄: m_age std::endl;}
private:std:string m_uid;std::string m_name;int m_age;
}int main() {Student stu;stu.print();
}这个时候你肯定有一个疑问创建出的这个学习信息没有赋值那怎么赋值呢这个时候你可能会想到在类中再定义一个API函数进行赋值如下
void setMessage(std::string uid, std::string name, int age) {m_uid uid;m_name name;m_age age;
}这样确实能够解决问题那如果每次都要这样不觉得太麻烦了么每次创建类都要额外在调用一个函数
因此C大叔也考虑到了这一点及发明出了构造函数这个“简单”的东西它允许我们再创建对象的时候自动调用该函数如我们将上面的学生类进行修改
#include iostreamclass Student
{
public:// 方法一Student(std::string uid, std::string name, int age){m_uid uid;m_name name;m_age age;}// 方法二推荐参数列表方法Student(std::string uid, std::string name, int age):m_uid(uid),m_name(name),m_age(age){}void print() {std::cout 学号: m_uid 姓名: m_name 年龄: m_age std::endl;}
private:std:string m_uid;std::string m_name;int m_age;
}int main() {Student stu(123456, wy, 18); // 创建时候赋值stu.print();
}这样写无论从逻辑上还是再写代码简约上都好很多。
构造函数特点:
构造函数名和类名相同构造函数可以重载构造函数没有返回类型声明
调用:
自动调用(隐式)一般默认情况下C编译器会自动调用构造函数(无参构造)手动调用(显示)在一些情况下则需要手工调用构造函数(有参构造)
析构函数
当对象释放时我们可能需释放/清理对象里面的某些资源如果再类中对某一个变量如成员变量申请了一块内存而在应对稍微复杂一点的项目就很容易忘记释放内存为了解决这个问题C提供了析构函数来处理对象的清理工作。析构函数和构造函数类似不需要用户来调用它而是在释放对象时自动执行。
特点:
析构函数名和类名相同但是得在前面加一个波浪号**~**析构函数只能有一个构造函数没有返回类型声明
构造/析构函数调用机制
当定义了多个对象时构造与析构的顺序是怎么样的呢
#include iostreamusing namespace std;class Test
{
public:Test(int id):m_id(id){cout m_id __FUNCTION__ endl;}~Test(){cout m_id __FUNCTION__ endl;}
private:int m_id;
};void test()
{Test t1(1);Test t2(2);
}int main()
{test();return 0;
}结果 结论:
先创建的对象先构造后创建的对象后构造先创建的对象后析构后创建的对象先析构
这个原因和函数调用内存有关函数调用是压栈和出栈的过程如果就想弄清楚请看计算机系统相关的书籍如csapp
析构函数调用时间
在该对象生命周期结束后调用
构造/析构函数用途展示
构造函数可以用来初始化对象而且不需要显式调用方便快捷
析构函数可以用来释放对象 一次写好没有后顾之忧(如经常忘记delete、free)
class Man
{
public:Man(){age 18;name new char[20]{0};strcpy(name,maye);}~Man(){if(name!nullptr){delete[] name;name nullptr;}}void print(){coutage nameendl;}
private:int age;char* name;
}这样就可以避免自己忘记释放内存的情况了。
构造函数分类
构造函数是可以重载的根据参数类型和作用可以分为以下几类
无参构造函数
直接创建对象即可自动调用Test te; 注意不要在对象后面加(),无参构造函数不能显式调用
有参构造函数 有三种调用方法 //1括号法
Test t1(20,cc);
t1.print();
//2赋值符号
Test t2 {18,wy};
t2.print();
//3匿名对象
Test t3 Test(90,wy);
t3.print();
//注意:
Test tt; //error:类Test不存在默认构造函数因为自己定义了构造函数//** 匿名对象如果没有值来接收那么就会被立即释放 **
Int(2, 3); //会立即释放Int f Int(2,3); //就不会立即释放如果没有写有参构造函数那么C编译器会自动帮我们生成一个无参构造函数如果写了有参构造函数那么就不会帮我们生成了必须自己写一个无惨构造函数才能直接定义对象。
拷贝构造函数(赋值构造) 用一个对象去初始化另一个对象时(函数传参也会拷贝)需要拷贝构造(如果自己没有写编译器会自动帮我们生成) Test t(1,2);
//1,赋值符号
Test t1 t;
//2,参数方法
Test t2(t);t2 t1; //这个调用的是赋值运算符重载函数注意定义之后进行赋值不会调用拷贝构造函数而是调用赋值函数这是运算符重载这个涉及到运算符重载的知识这个我们稍后讲解注意拷贝构造与运算符重载很容易搞混
移动构造函数
移动构造函数数用来实现移动语义转移对象之间的资源(如果自己没有写编译器会自动帮我们生成)调用std::move()
// 定义一个对象
Test t1(wy, 18);
Test t2(std::move(t1)); //移动构造这个时候对象t1所有权都转移给了t2t1没有了资源这样提高了资源的利用率移动std::move()这个东西我感觉很神奇没有结合实践感觉就这么回事但是一结合实际就会发现他特别伟大特别好用
深拷贝和浅拷贝
首先明确一点深拷贝和浅拷贝是针对类里面有指针的对象的因为基本数据类型在进行赋值操作时也就是拷贝是直接将值赋给了新的变量也就是该变量是原变量的一个副本这个时候你修改两者中的任何一个的值都不会影响另一个而对于对象来说在进行浅拷贝时只是将对象的指针复制了一份也就内存地址即两个不同的对象里面的指针指向了同一个内存地址那么在改变任一个对象的指针指向的内存的值时都是该变这个内存地址的所存储的值所以两个变量的值都会改变。
简单来说当数据成员中有指针时必须要用深拷贝。
浅拷贝shallowCopy只是增加了一个指针指向已存在的内存地址。 使用浅拷贝释放内存的时候可能会出现重复释放同一块内存空间的错误。 深拷贝deepCopy是增加了一个指针并且申请了一个新的内存使这个增加的指针指向这个新的内存。 使用深拷贝下释放内存的时候不会因为出现重复释放同一个内存的错误。
注意
C类中默认提供的拷贝构造函数是浅拷贝的要想实现深拷贝必须自己手动实现拷贝构造函数
//自己实现深拷贝
TString(const TString other) //普通右值引用
{if(other ! this) { // 不是自己拷贝自己m_size other.m_size;m_str new cahr[m_size 1];strcay(m_str,other.m_str);}
}int mian()
{TString other TString hello; //hello 为TString的一个实例化对象
}构造函数的初始化参数列表
初始化参数列表
当我们再构造函数进行赋值成员变量的时候可以有以下两种方法
class Student
{
public:// 方法一Student(std::string uid, std::string name, int age){m_uid uid;m_name name;m_age age;}// 方法二推荐参数列表方法不同变量之间用 ‘,’ 隔开 Student(std::string uid, std::string name, int age):m_uid(uid),m_name(name),m_age(age){}private:std:string m_uid;std::string m_name;int m_age;
}两种方法都可但是我比较喜欢第二种。
类中类如何构造
类的组合组合有时候叫聚合是将一个对象放到另一个对象里。它是一种 has-a 的关系。
简单来说就是一个类的对象作为另一个类的成员这就叫做类的组合。
那这个时候这么对每一个对象值赋值呢
假设我们再一个类B中创建了一个类A作为成员变量而且A类中成员变量中它只有一个带参数的构造函数没有默认构造函数。这时要对这个类成员进行初始化就必须调用这个类成员的带参数的构造函数
class A
{
public:A(int a) {int m_a a;}
private:int m_a;
}// 定义类B
class B
{
public:B(int b, int a):a1(a),m_b1 b{}private:int m_b1;A a1; // 创建A对象
}本类和对象成都需要执行构造函数那么谁先执行呢有什么样的顺序呢
先指针被组合对象的构造函数如果组合对象有多个按照定义顺序而不是按照初始化列表的顺序析构和构造顺序相反这个再上面将构造和析构函数有讲解如果大家忘了可以回去看一下哦