C++-构造与解析

构造函数

构造函数是与同类名的特殊成员函数,主要用来初始化对象的数据成员。

构造函数的特点:

  • 与类同名
  • 没有返回类型
  • 可以被重载
  • 由系统自动调用,不允许在程序中显示调用。
#include <iostream>
using namespace std;

class student{
private:
	string m_name;
	int m_age;
	int m_num;

public:
	student(const string &name, int age, int num) {
		m_name = name;
		m_age = age;
		m_num = num;
	}

	void Cout() {
		cout << "my name is " << m_name << "," << "my age is " << m_age << endl;
	}

};

int main(int argc, const char *argv[])
{
	student s("xiaoming", 18, 1234);
	s.Cout();
	return 0;
}

缺省构造函数

缺省构造函数也称无参构造函数,但其未必真的没有任何参数,为一个有参构造函数的每个参数都提供一个缺省值,同样可以达到无参构造函数的效果。

注意:

  • 如果一个类没有定义任何构造函数,那么编译器会为其提供一个缺省构造函数
  • 对基本类型的成员变量,不做初始化
  • 对类类型的成员变量(成员子对象),将自动调用相应类的缺省构造函数来初始化。
#include <iostream>
using namespace std;

class A{

public:
	A(void) {
		cout << "这是A的构造函数" << endl;
		m_t = 0;

	}
public:
	int m_t;
};

class B{
public:
	int m_j;//基本类型成员变量
	A m_a;//类类型成员变量(成员子对象)

};
int main(int argc, const char *argv[])
{
	B b;//调用成员对象m_a的无参构造函数 调用B的缺省构造函数
	cout << b.m_j << endl;
	cout << b.m_a.m_t << endl;

	
	return 0;
}

构造函数的重载

#include <iostream>
using namespace std;

class Desk{
private:
	int length, weight, hight, wide;
public:
	Desk(int l, int w, int h, int ww);
	void put() {
		cout << length << weight << hight << wide << endl;
	}

	Desk() {
		cout << "Desk(void)" << endl;
		length = 0;
		weight = 0;
		hight = 0;
		wide = 0;
	}
};

Desk::Desk(int l, int w, int h, int ww) {
	cout << "construct" << endl;
	length = l;
	weight = w;
	hight = h;
	wide = ww;
}
int main(int argc, const char *argv[])
{
	Desk s(1,2,3,4);
	s.put();
	Desk();
	
	return 0;
}

类型转换构造函数

将其他类型转换为当前类类型需要借助转换构造函数,转换构造函数只有一个参数。

#include <iostream>
#include <cstring>

using namespace std;

class test {

private:
	int m_i;
	string m_c;

public:
	test(void) {
		cout << "test(void)" <<endl;
		m_i = 0;
	}

	explicit test(int n) {
		cout << "test(int)" << endl;//类型转换构造函数
		m_i = n;
	}

	explicit test(const char *str) {
		cout << "test(char *)" << endl;
		m_i = strlen(str);
		m_c = str;
		cout << m_c << endl;
	}

	void printf() {
		cout << m_i << endl;
	}

};
int main(int argc, const char *argv[])
{
	test a1;
	a1.printf();

	test a = test(2);//编译器会找参数类型为int的构造参数
	a.printf();	

	test b = test("abc");//编译器会找参数类型为char *的构造参数
	b.printf();
	return 0;
}

explicit关键字,就是告诉编译器需要类型转换时,强制要求写成如下形式:

test a = test(2);
test a = 2;//会报错

拷贝构造函数

用一个已定义的对象构造同类型的副本对象,将调用该类的拷贝构造构造函数

class A{
    A(const A &that){//拷贝构造函数,注意参数必须是常引用
        ...
    }
};

A a;

A b(a);//调用拷贝构造

A c = a;//调用拷贝构造
#include <iostream>
using namespace std;

class A{
	
public:
	int m_t;
	A(int date = 0) {
		cout << "A(int)" << endl;
		m_t = date;
	}

	A(const A& that) {//拷贝构造函数
		cout << "A(const A&)" << endl;
		m_t = that.m_t;
	}
};
int main(int argc, const char *argv[])
{
	A a;
	A b(a);//编译器会调用拷贝构造函数
	A c = a;//调用拷贝构造函数
	
	return 0;
}
  • 如果一个类没有显式定义拷贝构造函数,那么编译器会为其提供一个缺省拷贝构造函数
  • 对基本类型成员变量,按字节复制
  • 对类类型成员(成员子对象),调用相应类型的拷贝构造函数
class User {
    string m_name;//调用string类的拷贝构造函数
    int m_age;//按字节复制
};
#include <iostream>
using namespace std;

class A{
	
public:
	int m_t;
	A(int date = 0) {
		cout << "A(int)" << endl;
		m_t = date;
	}

	A(const A& that) {//拷贝构造函数
		cout << "A(const A&)" << endl;
		m_t = that.m_t;
	}
};

class B{
public:
	A m_a;
};


int main(int argc, const char *argv[])
{
#if 0
	A a;
	A b(a);//编译器会调用拷贝构造函数
	A c = a;//调用拷贝构造函数

	B d;
#endif

	B b;
	b.m_a.m_t = 98;
	B b1;
	b1 = b;
	cout << b1.m_a.m_t << endl;//调用B的缺拷贝构造函数,由于有成员对象m_t,所以会调用A的拷贝构造函数

	return 0;
}

初始化列表

构造函数的初始化列表

构造函数对数据成员进行初始化还可以通过成员初始化列表的方式完成。语法格式为:

构造函数名(参数表):成员1(初始值参数),成员2(初始值参数){

}

例如:

#include <iostream>
using namespace std;

class Desk{
private:
	int length, weight, hight, wide;
public:
	Desk(int l, int w, int h, int ww);
	void put() {
		cout << length << weight << hight << wide << endl;
	}

	Desk() {
		cout << "Desk(void)" << endl;
		}
};

Desk::Desk(int l, int w, int h, int ww) :length(l), weight(w), hight(h), wide(ww){
	cout << "construct" << endl;
	length = l;
	weight = w;
	hight = h;
	wide = ww;
}
int main(int argc, const char *argv[])
{
	Desk s(1,2,3,4);
	s.put();
	Desk s2;
	
	return 0;
}

需要显式初始化列表的场景

一般而言,使用初始化列表和在构造函数体对成员变量进行赋初值,两者区别不大,可以人员一种,但是下面几种场景必须使用初始化列表:

  • 如果有类类型的成员变量(成员子对象),而该类有没有无参构造函数,则必须要通过初始化列表显式指明其初始化方式。
#include <iostream>
using namespace std;

class A{
private:
	int date_t;
public:
	A(int date) {
		date_t = date;
		cout << "A(int)" << endl;
	}
};

class B {
private:
	int m_t;
public:
	B(void) :m_t(123){
		cout << "B(void)" << endl;
		cout << m_t << endl;
	}
};
int main(int argc, const char *argv[])
{
	B b;//一定会去构造成员对象m_t,未指定如何构造,系统去调用m_t的无参构造函数
	
	return 0;
}
  • 被const修饰的成员变量(常成员变量)必须要在初始化列表中初始化参数
  • 引用型成员变量必须要在初始化列表中初始化
#include <iostream>
using namespace std;

int num = 100;
class A{
public:
	int & m_t;
	const int m_c;
/*
 * error此种写法错误,无法const类型和引用类型无法这样初始化
	A(void) {
		m_t = num;
		m_c = 100;
	}
*/
	A(void):m_t(num),m_c(num) {
		cout << m_t << " " << m_c << endl;
	}
};

int main(int argc, const char *argv[])
{
	A a;
	
	return 0;
}

初始化顺序

类中成员变量按声明顺序依次被初始化,而与初始化中的顺序无关。

#include <iostream>
using namespace std;

class A{
public:
	A(int a) {

		cout << "A(construct)" << endl;
	}
};

class B{
public:
	B(int b) {
		cout << "B(construct)" << endl;
	}
};

class C{
private:
	A m_a;
	B m_b;
public:
	C(int a, int b): m_b(b), m_a(a) {
	}
};


int main(int argc, const char *argv[])
{
	C c(2,3);
	
	return 0;
}

this指针

什么是this指针

#include <iostream>
using namespace std;

class stu{
private:
	int age;
	string name;

public:
	stu(int m_age, string m_name) {//编译完成之后就是stu(stu *this, int m_age, string m_name);
		age = m_age;
		name = m_name;
	}

	void cprintf() {
		cout << name << ":" << age << endl;
	}
};
int main(int argc, const char *argv[])
{
	stu zs(18,"zhangsan");//传递参数为(&zs,18,"zhangsan");
	stu ls(21,"lisi");//传递参数为(&ls, 21, "lisi");

	zs.cprintf();
	ls.cprintf();
	return 0;
}

this是一个用于标识对象自身的隐式指针,代表对象自身的地址。

在编译类成员函数时,C++编译器会自动将this指针添加到成员函数的参数表中。在用类的成员函数时,调用对象会把自己的地址通过this指针传递给成员函数。

this指针有什么用

需要显式使用this指针的常见场景:

  • 类中的成员变量和参数变量名字一样,可以通过this指针区分
  • 从成员函数中返回调用对象自身(返回自引用),支持链式调用
  • 在成员函数中销毁对象自身(对象自销毁)
#include <iostream>
using namespace std;

class Count{
	
private:
	int count;

public:
	Count(int count = 0) {
		this->count = count;
	}

	Count &add(void) {
		++count;
		return *this;
	}

	void printf() {
		cout << count << endl;
	}

	void destroy() {
		cout << "this:" << this << endl;
		delete this;
	}
};

int main(int argc, const char *argv[])
{
	Count cnt;

	cnt.printf();
	cnt.add().add().add();
	cnt.printf();
	
	Count *pnt = new Count;
	pnt->printf();
	pnt->add();
	pnt->printf();

	pnt->destroy();
	cout << pnt << endl;
	return 0;
}

常成员函数

在C++中,为了禁止成员函数修改成员数据的值,可以将它设置为常成员函数。设置方法就是在函数体之前加上const关键字。

class X{
    void func(参数1, 参数2, ...) const {

    }
};
#include <iostream>
using namespace std;

class A {
private:
	int num,age;
	string name;
public:
	A(int num, const string &name, int age) {
		this->age = age;
		this->num = num;
		this->name = name;
	}
	void printf(void) const{
		cout << "i am " << name << "i am age is " << age << "num:" << num << endl;

	}

};
int main(int argc, const char *argv[])
{
	A a(8499, "zhangsan", 21);
	a.printf();
	
	return 0;
}

常函数的使用注意事项:

  • 常对象只能调用常函数,非常对象既可以调用非常函数,也可以调用常函数
  • 函数名和形参表相同的成员函数,常版本和非常版本可以构成重载
    • 常对象只能选择常版本
    • 非常对象优先选择非常版本
  • 被mutable修饰的成员可以常函数中修改(了解)
#include <iostream>
using namespace std;

class A{
private:
	int n,m;
public:
	A(int nn = 1, int mm = 2):n(nn),m(mm) {};

	void func(void) {
		cout << "this is 11" << endl;
	}

	void bar(void) const{
		cout << "this is 15"<< endl;
	}

	void func(void) const{
		cout << "this is 19" << endl;
	}
};
int main(int argc, const char *argv[])
{
	A a;
	a.func();
	a.bar();

	const A b;
	b.func();
	
	return 0;
}

析构函数

析构函数是与类同名的另一个特殊成员函数,作用与构造函数相反,用于对象生存期结束时,完成对象的清理工作。析构函数的名称是:~类名

class X{
public:
    X() {//构造函数
    }

    ~X() {//析构函数

    }
};

析构函数的特点:

  • 无参无返回值
  • 不能重载
  • 只能由系统调用,不能显示调用
    • 栈对象,离开其作用域时析构函数自动调用
    • 堆对象,执行delete操作时析构函数自动调用
#include <iostream>
using namespace std;

class Integer{
private:
	int *m;

public:
	Integer(int i = 0) {
		m = new int(i);
	}

	~Integer(void) {
		cout << "析构函数" << endl;
		delete m;
	}

	void printf(void) {
		cout << *m << endl;
	}
};
int main(int argc, const char *argv[])
{
	if(1) {
		Integer a(100);
		a.printf();
		cout << "test1" << endl;

		Integer *pi = new Integer(200);

		pi->printf();
		delete pi;

		cout << "test2" << endl;
	}
	cout << "test3" << endl;

	return 0;
}

缺省析构函数

如果类没有显式定义析构函数,那么编译器会为其提供一个缺省析构函数,缺省析构的函数体为空,在析构函数体执行完毕后,类中的成员会被自动销毁。

  • 对基本类成员变量,什么也不做
  • 对类类成员变量(成员子对象),将会自动调用相应类的析构函数。
#include <iostream>
using namespace std;

class A{
public:
	A(void) {
		cout << "A(void)" << endl;
	}
	~A(void) {
		cout << "~A(void)" << endl;
	}

};

class B{
public:
	B(void) {
		cout << "B(void)" << endl;
	}
	~B(void) {
		cout << "~B(void)" << endl;
	}
	A a;

};

int main(int argc, const char *argv[])
{
	B b;
	
	return 0;
}

对象的穿件和销毁的过程

对象创建

  • 分配内存
  • 构造成员对象
  • 调用构造函数

对象销毁

  • 调用析构函数
  • 析构成员对象
  • 释放内存
  • 47
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STL是指标准模板库(Standard Template Library),它是C++语言的一部分,提供了一系列的模板类和函数,用于支持通用的数据结构和算法。STL的目标是提供高效、可重用和可扩展的组件,以便开发人员能够更轻松地编写高质量的代码。STL包含了许多常见的数据结构,如vector、list、set、map等,以及各种算法,比如排序、查找、遍历等。通过使用STL,开发人员可以更加高效地处理各种数据结构和算法的问题,提高代码的开发效率和质量。 在STL中,我们可以使用各种容器来存储和管理数据。例如,我们可以使用std::map来创建一个键值对的映射,其中每个键都有一个与之相关联的值。下面是一个示例代码,展示了如何创建和使用一个std::map对象: std::map<std::string, int> disMap() { std::map<std::string, int> tempMap{ {"C语言教程",10},{"STL教程",20} }; return tempMap; } std::map<std::string, int> newMap(disMap()); 在这个示例中,disMap()函数创建了一个临时的std::map对象,并初始化了其中的一些键值对。然后,使用移动构造函数将这个临时对象移动到了一个新的std::map对象newMap中。最终,我们可以通过newMap对象来访问和操作这些键值对。 综上所述,STL是C++中的标准模板库,提供了一系列的模板类和函数,用于支持通用的数据结构和算法。STL的使用可以提高代码的开发效率和质量,并且通过各种容器和算法,可以方便地处理各种数据结构和算法的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ STL详解超全总结(快速入门STL)](https://blog.csdn.net/qq_50285142/article/details/114026148)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【C++实验】阅读STL源码并分析](https://blog.csdn.net/qq_35760825/article/details/125311509)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值