栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++简单问题搞复杂之构造函数与初始化(普通类)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++简单问题搞复杂之构造函数与初始化(普通类)

夸张一点说,C语言是最容易掌握的程序设计语言。

简单和复杂往往却没有明确的界限,比如C创建结构体后,由于分配的内存没有做任何处理,接着就是一堆赋值语句,导致代码行数激增,那些鼠标滚轮经常坏掉的程序员,很大一部分原因皆是此类;

行数多就多呗,语法简约就是效率、直观、自由;

没错,然而人之初,性本惰,那些厌倦了malloc和calloc的人,外加其它原因,开始转向C++程序设计,于是有此文

使用calloc分配内存后初始化为零,不是初始化吗?

是,结构体的初始值,并非都是一样的,如:

	SECURITY_ATTRIBUTES sa;
	// assignment
	sa.nLength = sizeof(sa);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

显然,C++为偷懒又达到目的提供了很多帮助。

初入C++的童鞋,最先面临的就是构造函数的问题,等一下,构造函数不就是创建时自动调用的函数吗,目的明确何来疑问?

1.调用的究竟是哪个构造函数?

2.基类和成员调用的是哪个构造函数?

3.移动、拷贝、赋值又是声明逻辑?

……

……

正如我所说,C++只会把问题搞复杂;

其实C++有几个标准,C++98/03,C++11,C++14,C++17,C++20等,可惜了,标准文档是付费才有的读(大约15美元);

所幸的是,标准草案是免费的,而且跟标准文档几乎一模一样,当然,考验英文水平的时刻,也随之到来,同时还有耐力的考验。

每当有童鞋问及,我也是链接回复,诸如:

后来我发现,几乎没有人看得下去,最大的原因是:字数太多了(相比C语言可以简化到一张纸)

那么,我们就换一个方式,试!

首先,我们定义一个基类,Base.hpp

	class Base {
	protected:
		// for further test
		char *ptr;
	public:
		Base() { printf("Base::Base(), this=%pn", this); }
		Base(const Base &rhs) { printf("Base::Base(const Base &rhs), this=%p, &rhs=%pn", this, &rhs); }
		Base(Base &&rhs) { printf("Base::Base(Base &&rhs), this=%p, &rhs=%pn", this, &rhs); }
		Base(char *p) { printf("Base::Base(char *p), this=%pn", this); this->ptr = p; }
		virtual ~Base() { printf("Base::~Base(), this=%pn", this); }
		Base &operator=(const Base &rhs)
		{
			printf("Base::operator=(const Base &rhs), this=%p, &rhs=%pn", this, &rhs);
			if (&rhs != this) {}
			return *this;
		}
		Base &operator=(Base &&rhs)
		{
			printf("Base::operator=(const Base &&rhs), this=%p, &rhs=%pn", this, &rhs);
			if (&rhs != this) {}
			return *this;
		}
	};

可以看出,C++有4个基本的构造析构函数,以及两个赋值操作;其中移动构造函数和移动赋值操作是C++11新增的:

Base(Base &&rhs);

Base &operator=(Base &&rhs);

注意printf函数需要stdio.h,然后是作为数据成员的类,MemberObject.hpp

	class MemberObject {
	public:
		MemberObject() { printf("MemberObject::MemberObject(), this=%pn", this); }
		MemberObject(const MemberObject &rhs) { printf("MemberObject::MemberObject(const MemberObject &rhs), this=%pn", this); }
		MemberObject(MemberObject &&rhs) { printf("MemberObject::MemberObject(MemberObject &&rhs), this=%pn", this); }
		virtual ~MemberObject() { printf("MemberObject::~MemberObject(), this=%pn", this); }
		MemberObject &operator=(const MemberObject &rhs)
		{
			printf("MemberObject::operator=(const MemberObject &rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
		MemberObject &operator=(MemberObject &&rhs)
		{
			printf("MemberObject::operator=(const MemberObject &&rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
	};

接着是创建对象的类,Object.hpp

	class Object : public Base {
	protected:
		MemberObject mo;
	public:
		Object() { printf("Object::Object(), this=%pn", this); }
		Object(const Object &rhs) { printf("Object::Object(const Object &rhs), this=%pn", this); }
		Object(Object &&rhs) { printf("Object::Object(Object &&rhs), this=%pn", this); }
		Object(char *p) { printf("Object::Object(char *p), this=%pn", this); }
		virtual ~Object() { printf("Object::~Object(), this=%pn", this); }
		Object &operator=(const Object &rhs)
		{
			printf("Object::operator=(const Object &rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
		Object &operator=(Object &&rhs)
		{
			printf("Object::operator=(const Object &&rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
		Object operator+(const Object &rhs) const
		{
			printf("Object::operator+(const Object &rhs), this=%pn", this);
			Object o1;
			return o1;
		}
	};

程序设计,前瞻性很重要,这里使用模板函数来测试不同创建方式的构造细节:

template
void test_cpp()
{
	printf("n");
	C o1;
	printf("n");
	C o2 = o1;
	printf("n");
	C o3(o1);
	printf("n");
	C o4(o1 + o2);
	printf("n");
	B *p1 = new C(nullptr);
	printf("n");
	o2 = o1;
	printf("n");
	o3 = o1 + o2;
	printf("n");
	delete p1;
	printf("n");
}

其中,B表示基类,C表示创建类,现在我们试试:

test_cpp();

结果:

Base::Base(), this=000000573E15F168
MemberObject::MemberObject(), this=000000573E15F178
Object::Object(), this=000000573E15F168

Base::Base(), this=000000573E15F198
MemberObject::MemberObject(), this=000000573E15F1A8
Object::Object(const Object &rhs), this=000000573E15F198

Base::Base(), this=000000573E15F1C8
MemberObject::MemberObject(), this=000000573E15F1D8
Object::Object(const Object &rhs), this=000000573E15F1C8

Object::operator+(const Object &rhs), this=000000573E15F168
Base::Base(), this=000000573E15EFF8
MemberObject::MemberObject(), this=000000573E15F008
Object::Object(), this=000000573E15EFF8
Base::Base(), this=000000573E15F1F8
MemberObject::MemberObject(), this=000000573E15F208
Object::Object(Object &&rhs), this=000000573E15F1F8
Object::~Object(), this=000000573E15EFF8
MemberObject::~MemberObject(), this=000000573E15F008
Base::~Base(), this=000000573E15EFF8

Base::Base(), this=00000159706A25D0
MemberObject::MemberObject(), this=00000159706A25E0
Object::Object(char *p), this=00000159706A25D0

Object::operator=(const Object &rhs), this=000000573E15F198

Object::operator+(const Object &rhs), this=000000573E15F168
Base::Base(), this=000000573E15EFF8
MemberObject::MemberObject(), this=000000573E15F008
Object::Object(), this=000000573E15EFF8
Base::Base(), this=000000573E15F348
MemberObject::MemberObject(), this=000000573E15F358
Object::Object(Object &&rhs), this=000000573E15F348
Object::~Object(), this=000000573E15EFF8
MemberObject::~MemberObject(), this=000000573E15F008
Base::~Base(), this=000000573E15EFF8
Object::operator=(const Object &&rhs), this=000000573E15F1C8
Object::~Object(), this=000000573E15F348
MemberObject::~MemberObject(), this=000000573E15F358
Base::~Base(), this=000000573E15F348

Object::~Object(), this=00000159706A25D0
MemberObject::~MemberObject(), this=00000159706A25E0
Base::~Base(), this=00000159706A25D0

Object::~Object(), this=000000573E15F1F8
MemberObject::~MemberObject(), this=000000573E15F208
Base::~Base(), this=000000573E15F1F8
Object::~Object(), this=000000573E15F1C8
MemberObject::~MemberObject(), this=000000573E15F1D8
Base::~Base(), this=000000573E15F1C8
Object::~Object(), this=000000573E15F198
MemberObject::~MemberObject(), this=000000573E15F1A8
Base::~Base(), this=000000573E15F198
Object::~Object(), this=000000573E15F168
MemberObject::~MemberObject(), this=000000573E15F178
Base::~Base(), this=000000573E15F168

总结:

Object o1;

调用的是默认构造函数,这个没有声明说的了;按基类,成员,派生类顺序调用构造函数,对基类的基类也是同理;

Object o2 = o1;

调用的是拷贝构造函数,而不是赋值操作符;正如我所说,C++只会把问题搞复杂;

Object o3(o1);

同理,调用拷贝构造函数;同时,基类和成员调用的是默认构造函数,只要不显式指定,都是这种构造方式;

Object o4(o1 + o2);

首先创建一个返回值对象,处理后以返回值为参数,调用移动构造函数,然后析构返回值对象;

等一下,堆栈对象不是函数返回时就自动析构了吗?

正如我所说,C++只会把问题搞复杂,你可以理解为,编译器对变量的管理自己实现了一套“引用计数”机制;

 Base *p1 = new C(nullptr);

调用自定义构造函数(带参构造),你可以自己定义很多个这样的函数,同样的,未指定的成员和基类会调用默认构造函数,由于析构函数是virtual,我们可以delete基类的指针来delete实际的Object,这里的virtual只是作区别标记,跟普通的虚函数并不一样,正如我所说,C++只会把问题搞复杂,这样的情形还有很多;

o2 = o1;

调用赋值操作符;

o3 = o1 + o2;

调用移动赋值操作符,首先创建返回值,创建临时Object,移动构造,析构返回值,移动赋值,析构临时Object,如下图:

 析构函数按派生类,成员,基类顺序调用,即与构造函数顺序相反。

那么,怎么让基类和成员调用一样的构造函数呢?

我们再创建一个类,其实是Object复制粘贴修改,Object2.hpp

	class Object2 : public Base {
	protected:
		MemberObject mo;
	public:
		Object2() { printf("Object2::Object2(), this=%pn", this); }
		Object2(const Object2 &rhs) : Base(rhs), mo(rhs.mo) { printf("Object2::Object2(const Object2 &rhs), this=%pn", this); }
		Object2(Object2 &&rhs) : Base(rhs), mo(rhs.mo) { printf("Object2::Object2(Object2 &&rhs), this=%pn", this); }
		Object2(char *p) : Base(p) { printf("Object2::Object2(char *p), this=%pn", this); }
		virtual ~Object2() { printf("Object2::~Object2(), this=%pn", this); }
		Object2 &operator=(const Object2 &rhs)
		{
			printf("Object2::operator=(const Object2 &rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
		Object2 &operator=(Object2 &&rhs)
		{
			printf("Object2::operator=(const Object2 &&rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
		Object2 operator+(const Object2 &rhs) const
		{
			printf("Object2::operator+(const Object2 &rhs), this=%pn", this);
			Object2 o1;
			return o1;
		}
	};

再次调用:

test_cpp();

结果

Base::Base(), this=000000573E15F168
MemberObject::MemberObject(), this=000000573E15F178
Object2::Object2(), this=000000573E15F168

Base::Base(const Base &rhs), this=000000573E15F198, &rhs=000000573E15F168
MemberObject::MemberObject(const MemberObject &rhs), this=000000573E15F1A8
Object2::Object2(const Object2 &rhs), this=000000573E15F198

Base::Base(const Base &rhs), this=000000573E15F1C8, &rhs=000000573E15F168
MemberObject::MemberObject(const MemberObject &rhs), this=000000573E15F1D8
Object2::Object2(const Object2 &rhs), this=000000573E15F1C8

Object2::operator+(const Object2 &rhs), this=000000573E15F168
Base::Base(), this=000000573E15EFF8
MemberObject::MemberObject(), this=000000573E15F008
Object2::Object2(), this=000000573E15EFF8
Base::Base(const Base &rhs), this=000000573E15F1F8, &rhs=000000573E15EFF8
MemberObject::MemberObject(const MemberObject &rhs), this=000000573E15F208
Object2::Object2(Object2 &&rhs), this=000000573E15F1F8
Object2::~Object2(), this=000000573E15EFF8
MemberObject::~MemberObject(), this=000000573E15F008
Base::~Base(), this=000000573E15EFF8

Base::Base(char *p), this=00000159706A2450
MemberObject::MemberObject(), this=00000159706A2460
Object2::Object2(char *p), this=00000159706A2450

Object2::operator=(const Object2 &rhs), this=000000573E15F198

Object2::operator+(const Object2 &rhs), this=000000573E15F168
Base::Base(), this=000000573E15EFF8
MemberObject::MemberObject(), this=000000573E15F008
Object2::Object2(), this=000000573E15EFF8
Base::Base(const Base &rhs), this=000000573E15F348, &rhs=000000573E15EFF8
MemberObject::MemberObject(const MemberObject &rhs), this=000000573E15F358
Object2::Object2(Object2 &&rhs), this=000000573E15F348
Object2::~Object2(), this=000000573E15EFF8
MemberObject::~MemberObject(), this=000000573E15F008
Base::~Base(), this=000000573E15EFF8
Object2::operator=(const Object2 &&rhs), this=000000573E15F1C8
Object2::~Object2(), this=000000573E15F348
MemberObject::~MemberObject(), this=000000573E15F358
Base::~Base(), this=000000573E15F348

Object2::~Object2(), this=00000159706A2450
MemberObject::~MemberObject(), this=00000159706A2460
Base::~Base(), this=00000159706A2450

Object2::~Object2(), this=000000573E15F1F8
MemberObject::~MemberObject(), this=000000573E15F208
Base::~Base(), this=000000573E15F1F8
Object2::~Object2(), this=000000573E15F1C8
MemberObject::~MemberObject(), this=000000573E15F1D8
Base::~Base(), this=000000573E15F1C8
Object2::~Object2(), this=000000573E15F198
MemberObject::~MemberObject(), this=000000573E15F1A8
Base::~Base(), this=000000573E15F198
Object2::~Object2(), this=000000573E15F168
MemberObject::~MemberObject(), this=000000573E15F178
Base::~Base(), this=000000573E15F168

嗯,似乎大部分情形都符合预期,除了:

也就是说,右值(临时的,无名称的) 在移动构造函数或移动赋值操作中,是左值!

正如我所说,C++只会把问题搞复杂!

那么,如何向成员和基类传递右值呢?

我们再创建一个类,其实是Object2的复制粘贴修改,Object3.hpp

	class Object3 : public Base {
	protected:
		MemberObject mo;
	public:
		Object3() { printf("Object3::Object3(), this=%pn", this); }
		Object3(const Object3 &rhs) : mo(rhs.mo), Base(rhs) { printf("Object3::Object3(const Object3 &rhs), this=%pn", this); }
		Object3(Object3 &&rhs) : Base(std::move(rhs)), mo(std::move(rhs.mo)) { printf("Object3::Object3(Object3 &&rhs), this=%pn", this); }
		Object3(char *p) : Base(p) { printf("Object3::Object3(char *p), this=%pn", this); }
		virtual ~Object3() { printf("Object3::~Object3(), this=%pn", this); }
		Object3 &operator=(const Object3 &rhs)
		{
			printf("Object3::operator=(const Object3 &rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
		Object3 &operator=(Object3 &&rhs)
		{
			printf("Object3::operator=(const Object3 &&rhs), this=%pn", this);
			if (&rhs != this) {}
			return *this;
		}
		Object3 operator+(const Object3 &rhs) const
		{
			printf("Object3::operator+(const Object3 &rhs), this=%pn", this);
			Object3 o1;
			return o1;
		}
	};

这里顺便打乱了构造函数后面的冒号的初始化列表,因为这个东西对C语言的童鞋来说,很不自在,而且会有疑惑,初始化列表是不是顺序执行的;

执行:

test_cpp();

结果

Base::Base(), this=000000573E15F168
MemberObject::MemberObject(), this=000000573E15F178
Object3::Object3(), this=000000573E15F168

Base::Base(const Base &rhs), this=000000573E15F198, &rhs=000000573E15F168
MemberObject::MemberObject(const MemberObject &rhs), this=000000573E15F1A8
Object3::Object3(const Object3 &rhs), this=000000573E15F198

Base::Base(const Base &rhs), this=000000573E15F1C8, &rhs=000000573E15F168
MemberObject::MemberObject(const MemberObject &rhs), this=000000573E15F1D8
Object3::Object3(const Object3 &rhs), this=000000573E15F1C8

Object3::operator+(const Object3 &rhs), this=000000573E15F168
Base::Base(), this=000000573E15EFF8
MemberObject::MemberObject(), this=000000573E15F008
Object3::Object3(), this=000000573E15EFF8
Base::Base(Base &&rhs), this=000000573E15F1F8, &rhs=000000573E15EFF8
MemberObject::MemberObject(MemberObject &&rhs), this=000000573E15F208
Object3::Object3(Object3 &&rhs), this=000000573E15F1F8
Object3::~Object3(), this=000000573E15EFF8
MemberObject::~MemberObject(), this=000000573E15F008
Base::~Base(), this=000000573E15EFF8

Base::Base(char *p), this=00000159706A1DF0
MemberObject::MemberObject(), this=00000159706A1E00
Object3::Object3(char *p), this=00000159706A1DF0

Object3::operator=(const Object3 &rhs), this=000000573E15F198

Object3::operator+(const Object3 &rhs), this=000000573E15F168
Base::Base(), this=000000573E15EFF8
MemberObject::MemberObject(), this=000000573E15F008
Object3::Object3(), this=000000573E15EFF8
Base::Base(Base &&rhs), this=000000573E15F348, &rhs=000000573E15EFF8
MemberObject::MemberObject(MemberObject &&rhs), this=000000573E15F358
Object3::Object3(Object3 &&rhs), this=000000573E15F348
Object3::~Object3(), this=000000573E15EFF8
MemberObject::~MemberObject(), this=000000573E15F008
Base::~Base(), this=000000573E15EFF8
Object3::operator=(const Object3 &&rhs), this=000000573E15F1C8
Object3::~Object3(), this=000000573E15F348
MemberObject::~MemberObject(), this=000000573E15F358
Base::~Base(), this=000000573E15F348

Object3::~Object3(), this=00000159706A1DF0
MemberObject::~MemberObject(), this=00000159706A1E00
Base::~Base(), this=00000159706A1DF0

Object3::~Object3(), this=000000573E15F1F8
MemberObject::~MemberObject(), this=000000573E15F208
Base::~Base(), this=000000573E15F1F8
Object3::~Object3(), this=000000573E15F1C8
MemberObject::~MemberObject(), this=000000573E15F1D8
Base::~Base(), this=000000573E15F1C8
Object3::~Object3(), this=000000573E15F198
MemberObject::~MemberObject(), this=000000573E15F1A8
Base::~Base(), this=000000573E15F198
Object3::~Object3(), this=000000573E15F168
MemberObject::~MemberObject(), this=000000573E15F178
Base::~Base(), this=000000573E15F168

掐指一算,以及有很多人打瞌睡,部分人关闭了网页,所以简要总结:

1.使用std::move()转换为右值,需要#include

2.std::move()本质是static_cast<>()

3.初始化列表只提供列表,顺序仍然是基类,成员,派生类顺序构造

下一篇讲一讲模板相关的鲜为人知,不得不说,不可描述,难以启齿的故事

(待续)

暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1015093.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号