C++构造函数/拷贝控制

2023-10-27

1. C++构造函数:

C++中构造函数(constructor)的作用是用来控制类对象的初始化过程。构造函数的作用是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

1.1 默认构造函数:

当定义一个类对象时,如果没有为对象提供初始值,则此时会执行默认初始化,当执行默认初始化时将调用类的 “默认构造函数”(default constructor)默认构造函数无需任何实参,形式如下:

class Sales_data {
public:
	Sales_data() {}		//默认构造函数 (无需任何实参)
private:
	std::string		bookNo;
	unsigned int 	units_sold = 0;
	double 			revenue = 0;	
};

1.2 合成的默认构造函数:

编译器自动创建的默认构造函数 称为 “合成的默认构造函数”(synthesized default constructor)

合成的默认构造函数将按照如下规则初始化类的数据成员:
(1)如果存在类内初始值,则用初始值来初始化成员;
(2)否则,默认初始化成员。

(例如 Sales_data 例子中的 units_sold 和 revenue 数据成员都提供了初始值,而 revenue 没有提供初始值)

注意:
不要过度依赖编译器提供的合成的默认构造函数,原因如下:
(1)一旦类中定义了任一类型的其他的构造函数,则编译器将不会生成 合成的默认构造函数,此时如果我们又没有显式的定义一个默认构造函数,将会造成类没有默认构造函数;
(这种情况在编译时将会报错:“no matching constructor for initialization”)

(2)对于某些类来说,合成的默认构造函数可能会执行错误的操作,例如对于局部变量的默认初始化,其值是未定义的;

(3)有些时候编译器无法为某些类生成合成的默认构造函数,例如某些类中包含没有默认构造函数的成员。

=default 声明符:

如果我们定义了其他形式的构造函数,而又需要编译器为我们生成合成的默认构造函数时,可以使用 =default:

class Sales_data {
public:
	Sales_data() = default;		//编译器会生成合成的默认构造函数
	Sales_data(unsigned int sold) : units_sold(sold) {}
private:
	std::string		bookNo;
	unsigned int 	units_sold = 0;
	double 			revenue = 0;
};

另外一点需要注意,C++类中不能同时共存默认构造函数和全部指定数据成员默认值的构造函数。例如下面的例子中编译时就会出错:

class Sales_Item {
public:
	Sales_Item() {}		//默认构造函数
	Sales_Item(int x = 1, int y = 2) : a(x), b(y) {}	//所有数据成员都指定初始值的构造函数
private:
	int a;
	int b;
};

------
//编译时会出现二义性错误:
error: call to constructor of 'Sales_Item' is ambiguous

1.3 隐式的类类型转换:

如果构造函数 只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,这种构造函数被称作为 “转换构造函数”(converting constructor)

例如:

class Sales_data {
public:
	Sales_data() = default;

	Sales_data(std::string s, unsigned cnt, double price) : bookNo(s), units_sold(cnt), revenue(cnt*price) {}

	Sales_data(std::string s) : bookNo(s) { cout << "func constructor" << endl; }	
	
	void combine(const Sales_data& item) { cout << "func combine" << endl; }

private:
	std::string		bookNo;
	unsigned int 	units_sold;
	double 			revenue;
};


int main() {
    std::string null_book = "9-999-99999-9";
    
    Sales_data item;
	item.combine(null_book);
	//此时发生隐式转换:string类型的null_book隐式转换为Sales_data类型
	//!!编译器会使用给定的string对象自动创建一个Sales_data类型的【临时对象】

    return 0;
}

运行结果:

func constructor		//先调用Sales_data(string)这个构造函数 通过 null_data参数构造一个Sales_data类型的临时对象
func combine			//将上面生成的临时对象传入给combine()函数

1.3.1 关于隐式转换需要注意的几点:

① 注意在上面的例子中,当发生隐式转换时,编译器会使用给定的string对象自动创建一个Sales_data类型的 临时对象,而临时对象只能绑定到const类型的数据上(因为临时对象的值不能被修改),所以如果需要隐式转换,combine()函数的形参必行为const类型,如果没有const关键字,编译时将会报错:

error: non-const lvalue reference to type 'Sales_data' cannot bind to a value of unrelated type 'std::__1::string'

② 另外需要的是,只允许一步类型类类型转换

1.3.2 explicit 关键禁止隐式类型转换:

当需要明确禁止类发生类型转换时,可通过将构造函数声明为explicit加以阻止。

class Sales_data {
public:
	Sales_data() = default;

	Sales_data(std::string s, unsigned cnt, double price) : bookNo(s), units_sold(cnt), revenue(cnt*price) {}

	//使用explicit禁止使用此构造函数发生隐式的类型转换:
	explicit Sales_data(std::string s) : bookNo(s) {}	
	
	void combine(const Sales_data& item) {}

};

1.3.3 关于explicit关键字的注意事项:

① 关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,所以无须将这些构造函数指定为explicit的;

② 只能在类内 声明 构造函数时使用explicit关键字,在类外定义时不应重复;

③ explicit构造函数只能用于直接初始化:

Sales_data item1(null_book);	//正确:直接初始化
Sales_data item2 = null_book;	//错误:不能将explicit构造函数用于拷贝形式的初始过程

④ 声明为explicit的构造函数不能用于隐式类型转换,但可以用于显式的强制类型转换:

//正确:实参是一个显式构造的Sales_data对象
item.combine(Sales_data(null_book));

item.combine(static_cast<Sales_data>(null_book));

1.3.4 小结:关于隐式转换 和 explicit关键字:

对于类类型的隐式类型转换,其本质就是调用类中的某个只含有一个实参的构造函数,通过入参构造一个临时的类类型对象。
所以explicit关键字禁止隐式转换的机理就是限制这个可能被用于构造临时对象的构造函数,使其不能被隐式调用。

1.4 拷贝构造函数:

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数是 “拷贝构造函数”(copy constructor)

class Foo {
public:
	Foo();
	Foo(const Foo& );
};

拷贝构造函数的第一个参数必须是引用类型
② 虽然我们可以定义一个接受非const引用的拷贝构造函数,但此参数几乎总是一个const的引用;
③ 拷贝构造函数通常不应该是explicit的,因为拷贝构造函数几种情况下都会被隐式地使用;
④ 如果类型没有显式的定义拷贝构造函数,则编译器会为我们生成一个 “合成的拷贝构造函数”(synthesized copy constructor)

class Sales_data {
public:
	Sales_data(const Sales_data& orig);
private:
	std::string     bookNo;
	int				units_sold;
	double			revenue = 0.0;
};

Sales_data::Sales_data(const Sales_data& orig) : bookNo(orig.bookNo), units_sold(orig.units_sold), revenue(orig.revenue) {}

1.4.1 “拷贝初始化” 与 “直接初始化” 的区别:

//以一个string类型的变量的初始化为例:
string dots(10, '0');		//直接初始化
string s(dots);				//直接初始化

string s2 = dots;					//拷贝初始化
string null_book = "9-999-99-9";	//拷贝初始化
string nines = string(100, '9');	//拷贝初始化

当使用 直接初始化 时,我们实际上是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数(可能是默认构造函数,可能是带参数的构造函数);

当使用 拷贝初始化 时,我们要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换。

1.5 拷贝赋值运算符:

拷贝赋值运算符实质上是对“=”运算符的重载,而运算符的重载本质上是函数。

class Foo {
public:
	Foo& operator=(const Foo& orig) { }
};

为了与内置类型的赋值保持一致,赋值运算符通常会返回一个指向其左侧运算对象的引用。

与处理拷贝构造函数一样,如果一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个“合成的拷贝赋值运算符”(synthesized-assignment operator)。

2. C++会隐式的生成哪些构造函数:

编译器可以隐式的为类创建 default默认构造函数、copy拷贝构造函数、copy assignment赋值操作符、析构函数。

其中,合成的构造函数和析构函数,主要供编译器用来放置一些必要的代码,如对成员变量(非静态)的默认构造函数、放置基类的构造函数(如果此类是一个派生类) 等;析构函数则反之(合成的析构函数是非virtual)。

至于 拷贝构造函数和拷贝赋值运算符,编译器合成的版本只是简单复制(浅拷贝)。

有两种情况,编译器会拒绝生成合成的拷贝赋值运算符:
① 赋值操作不合法:
例如:

class NameObject {
public:
	//NameObject() = default;
	NameObject(string &name) : nameValue(name) {}
private:
	string& nameValue;
};

对于NameObject类,编译器将拒绝生成合成的拷贝赋值运算符,程序可以编译通过,但类中没有拷贝赋值运算符,当使用此类型对象进行拷贝赋值操作时,编译器将会报错:

error: object of type 'NameObject' cannot be assigned
      because its copy assignment operator is implicitly deleted

实际上,对于NameObject类,编译器同样也会拒绝为其生成default默认构造函数。如果我们要求编译器为我们合成默认构造函数,编译时将会报错:

warning: explicitly defaulted default constructor is
      implicitly deleted

② 当基类中的 “operator=” 是private,则派生类的“operator=” 编译器将拒绝合成。

3. 如何禁止编译器自动生成构造函数:

3.1 方法一:将构造函数声明为private:

如果不想让你声明的类类型的对象被拷贝或赋值,则需要禁止编译器生成合成的拷贝构造函数和拷贝赋值运算符。

有一种方法可以禁止编译器生成合成的构造函数:
由于编译器生成的构造函数都是public,所以为了阻止这些函数被创建出来,我们可以将拷贝构造函数或拷贝赋值运算符声明为private,而不定义它们。

例如:

class HomeForSale {
public:
	...
private:
	HomeForSale(const HomeForSale&);
	HomeForSale& operator=(const HomeForSale&);
};

3.2 方法二:使用 “=delete” 声明符 :

在C++11中,引入了 “=delete” 声明符,专门用于明确禁止不要生成合成的构造函数。例如:

class HomeForSale {
public:
	HomeForSale(const HomeForSale&) = delete;
	HomeForSale& operator=(const HomeForSale&) = delete;
private:

};

4. 关于构造函数的一些注意事项:

4.1 别让异常逃离析构函数:

(1)析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获任何异常,然后吞下它们(不传播)或结束程序。
(2)如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么 class 应该提供一个普通函数(而非在析构函数中)执行该操作。

4.2 不要在构造和析构过程中调用virtual函数:

在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)。

4.3 令operator= 返回一个 reference to *this

令“operator=”赋值运算符返回一个“引用类型”只是一种约定俗成的做法,并无强制性。 如果不遵循它,而是改为返回“值结果”,代码一样可以编译通过 且正常运行。
然而这份协议被所有内置类型和标准程序库(STL)提供的类型如 string、vector 等 共同遵守。因此一般在自定义类型时也会遵守这一协议,即令 拷贝赋值运算符operator= 返回一个引用类型。

回顾拷贝赋值运算符的写法:

class Base {
public:
	Base& operator=(const Base& rhs) {
		this->num = rhs.num;
		return *this;
	}
private:
	int num;
};

拷贝赋值运算符的本质上是对“=”运算符的重载,对于类对象的“+”加法运算的重载,写法为:

//1. 当 operator+ 是非成员函数 :
Base operator+(const Base& a, const Base& b) {
	return Base(a.num + b.num);
}
//此时不能返回Base&,因为temp是函数内的局部变量,退出作用域后将被销毁

//2. 当 operator+ 是成员函数:
class Base {
public:
	Base operator+(const Base& rhs) {
		return Base(this->num + rhs.num);
	}
private:
	int num;
};

4.4 在operator= 中处理“自我赋值”:

(1)确保当对象自我赋值时operator= 有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
(2)确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

4.5 复制对象时勿忘其每一个成分:

(1)Coping函数应该确保复制“对象内的所有成员变量”及“所有base class成分”。
(2)不要尝试以某个coping函数实现另一个coping函数。应该将共同机能放进第三个函数中,并由两个coping函数共同调用。

第一点:对于自行编写的 copy constructor、 copy assignment,应该保证所有的数据成员都被拷贝赋值。但是,如果遗漏了某个成员没有被拷贝赋值,此时编译器不会报错,只能靠程序员自行保证。
特别是对一个类新增了成员时,一定要记得更新对应的拷贝构造函数和拷贝赋值运算符。

第二点:对于有继承关系的拷贝构造,派生类中的拷贝构造函数不会自动调用基类的拷贝构造函数,如果你没有在派生类拷贝构造函数的初始值列表中显式的调用基类的拷贝构造函数,则编译器会调用基类的default默认构造函数,这时的初始值可能并非预期。

当在派生类的拷贝构造函数中 调用 基类的拷贝构造函数时,直接传入 派生类类型的引用即可,基类会将其阶段。
例如:

class Base {
public:
	Base(const Base& rhs) : num(rhs.num) {}
private:
	int num;
};

class Derived : public Base {
public:
	Derived(const Derived& rhs) : Base(rhs), len(rhs.len) {}
private:
	int len;
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++构造函数/拷贝控制 的相关文章

  • 我应该把 try/catch 和“using”语句放在哪里? [复制]

    这个问题在这里已经有答案了 可能的重复 try catch using 正确的语法 https stackoverflow com questions 4590490 try catch using right syntax 我想try c
  • 编写此代码片段的有效方法是什么? [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 更有效和 或更短地重写此代码以节省字节并显得不那么冗长的方法 if N 2 0 N 6 N 8 N 10 N 12 N 14 N 16 N
  • 元组在 VS2012 中如何工作?

    Visual Studio 2012 功能 tuples但不是可变参数模板 这是如何完成的 如何在不使用可变模板的情况下实现元组 简而言之 微软做了与之前在 NET 中实现类似元组的数据类型完全相同的事情 创建许多版本 每个版本都有固定数量
  • 锁定 ASP.NET 应用程序变量

    我在 ASP NET 应用程序中使用第三方 Web 服务 对第 3 方 Web 服务的调用必须同步 但 ASP NET 显然是多线程的 并且可能会发出多个页面请求 从而导致对第 3 方 Web 服务的同时调用 对 Web 服务的调用封装在自
  • 司机和提供商之间的区别

    数据库中的驱动程序和提供程序有什么区别 有没有解释一下 不胜感激 样本 ADO NET driver for MySQL vs providerName System Data EntityClient 来自 MSDN 论坛 驱动程序是安装
  • 将设置函数(setter)标记为 constexpr 的目的是什么? [复制]

    这个问题在这里已经有答案了 我无法理解将 setter 函数标记为的目的constexpr 自 C 14 起这是允许的 我的误解来自以下情况 我使用 constexpr c tor 声明一个类 并且我将通过创建该类的 constexpr 实
  • 在 ASP.NET MVC 中将模型从视图传递到控制器

    我正在 ASP NET MVC 中开发我的第一个应用程序 但遇到了一个我无法解决的问题 即使在阅读了整个互联网之后也是如此 因此 我有几个使用视图模型创建的视图 它们是报告 这些视图模型是根据用户选择标准填充的 我正在尝试构建一种接受模型并
  • 如何以编程方式播放 16 位 pcm 数组 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有一个包含 16 位 pcm 值的短 数组 我希望能够在不添加任何标题 也不将任何文件保存到内存的情况下播放它 我知道我可能需要一个提供
  • 全局使用和 .NET Standard 2.0

    我最近意识到我可以使用 C 10 功能文件范围的命名空间在 NET Standard 2 0 项目中也可以通过设置
  • 如何在win32中使用GetSaveFileName保存文件?

    我编写此代码是为了获取 fileName 来保存我的文件 include stdafx h include
  • 在 C# 中何时使用 ArrayList 而不是 array[]?

    我经常使用一个ArrayList而不是 正常 array 当我使用时 我感觉好像我在作弊 或懒惰 ArrayList 什么时候可以使用ArrayList在数组上 数组是强类型的 并且可以很好地用作参数 如果您知道集合的长度并且它是固定的 则
  • 通过 C# Mailkit / Mimekit 发送电子邮件,但出现服务器证书错误

    Visual Studio 2015 中的 0 代码 1 我正在使用 Mailkit 最新版本 1 18 1 1 从我自己的电子邮件服务器发送电子邮件 2 电子邮件服务器具有不受信任的自签名证书 3 我在代码中添加了以下两行 以忽略服务器证
  • 用于连接 DataTable 上的动态列的动态 LINQ

    我目前遇到的情况不确定如何继续 我有两个从数据库填充的数据表 我还有一个可用的列名称列表 可用于将这两个数据表连接在一起 我希望编写一组 LINQ 查询 这些查询将 显示两个数据表中的行 内部联接 用于从一个数据表更新另一个数据表 显示一个
  • 如何使用递归查找数字中的最小元素 [C]

    好的 所以我正在准备我的 C 考试 当谈到递归时我有点卡住了我是大学一年级的学生 这对我来说似乎有点困难 练习要求在给定的数字中使用递归函数我需要找到最小的元素 例如 52873 是 2 程序需要打印 2 include
  • Resharper:IEnumerable 的可能多重枚举

    我正在使用新的 Resharper 版本 6 在我的代码中的几个地方 它给一些文本加了下划线 并警告我可能存在IEnumerable 可能的多重枚举 我理解这意味着什么 并在适当的情况下采纳了建议 但在某些情况下 我不确定这实际上是一个大问
  • 如何在 C# 中获取 Json 数组?

    我有一个像这样的 Json 字符串 我想将它加载到 C 数组中 当我尝试这样做时 我收到异常 我的字符串 customerInformation customerId 123 CustomerName Age 39 Gender Male
  • C++ 到 C# 事件处理

    所以我有我的C WinForm 应用程序 我从中调用我的C CLI MFC dll图书馆 但也有一些events在我的 C 库上 甚至此事件也发生在该库的本机 非 CLI 部分 我需要从我的 C 应用程序调用一些代码 并获取一些有关此事件的
  • C 语言中的 Alpha 混合 2 RGBA 颜色[重复]

    这个问题在这里已经有答案了 可能的重复 如何快速进行阿尔法混合 https stackoverflow com questions 1102692 how to do alpha blend fast 对 2 个 RGBA 整数 颜色进行
  • 使用 C# 动态创建按钮并按预定义的顺序放置它们

    NET 4 5 C 创建 Windows 窗体 我想动态创建和添加按钮并为其分配单击事件 但希望它们以特定的方式动态放置 就像图像一样 我的问题是如何以上述方式动态放置按钮 即 4x4 格式 一行 4 个按钮 4 列 但行数不受限制 是否可
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost

随机推荐

  • 使用Python,OpenCV制作图像Mask——截取ROIs及构建透明的叠加层

    使用Python OpenCV制作图像Mask 截取ROIs及构建透明的叠加层 1 效果图 2 源码 参考 这篇博客将介绍如何使用OpenCV制作Mask图像掩码 使用位运算和图像掩码允许我们只关注图像中感兴趣的部分 截取出任意区域的ROI
  • web自动化测试框架搭建(python+selenium+pytest+pom+ddt)

    本篇文件利用当下流行的pom设计模式设置测试框架 编写测试用例 生成测试报告 并最终jenkins集成 一 selenium selenium是一个开源的web ui自动化测试工具 详细就不再过多介绍了 二 环境搭建 关于环境搭建 非常简单
  • kubernetes使用ansible快速构建集群

    软硬件限制 1 cpu和内存 master 至少1c2g 推荐2c4g node 至少1c2g 2 linux系统 内核版本至少3 10 推荐CentOS7 RHEL7 3 docker 至少1 9版本 推荐1 12 4 etcd 至少2
  • ES计算指定索引,按多个字段去重后批量查询count结果

    ElasticSearch批量查询各个字段去重后的结果代码实现 计算指定索引 批量按多个字段去重后批量查询count结果 计算指定索引 批量按多个字段去重后批量查询的count结果 param indexName param distinc
  • 关于计算重叠四边形的面积的算法

    一 计算矩形重叠面积的三种方法 方法1 两个矩形的宽之和 减去组合之后的宽就得到重叠区域的宽 高同理 def IOU Reframe GTframe 自定义函数 计算两矩形 IOU 传入为均为矩形对角线 x y 坐标 x1 Reframe
  • 30天自制操作系统——第一天到第二天

    第一天 光盘地址用的这个 30天自制操作系统光盘 夕雨714 博客园 cnblogs com Bz162下载地址 Bz c mos vcraft jp 启动方式 D 文档 学习科目 计算机基础 操作系统 操作系统实验 30dayMakeOS
  • JavaWeb项目(登录注册页面)全过程详细总结

    JavaWeb项目 登录注册页面 全过程总结 文章目录 JavaWeb项目 登录注册页面 全过程总结 一 环境准备与开发工具 二 创建 JavaWeb 项目 2 1 新建Dynamic Web Project项目 2 2 创建前端页面 2
  • 手机Vbus上防护用OVP简介

    手机Vbus上防护用OVP简介 作者 AirCity 2019 10 19 aircity007 sina com 1 什么是OVP OVP是Over Voltage Protection的首字母缩写 中文名是过压保护负载开关 当输入电压大
  • CentOS安装教程-解决“Warning:/dev/root does not exist”问题

    在安装CentOS时 若出现 Warning dev root does not exist could not boot 一般情况下是因为未找到安装系统盘的所在位置 例如 U盘 这时只需找到其位置 并对配置稍作修改即可 当我们使用U盘安装
  • LeetCode:二叉树的修改与构造(5道经典题目)

    LeetCode 二叉树的修改与构造 5道经典题目 本文带来与二叉树的修改与构造有关的经典题目 主要实现是C 226 翻转二叉树 106 从中序与后序遍历序列构造二叉树 105 从前序与中序遍历序列构造二叉树 654 最大二叉树 617 合
  • 安装报错ERROR: Could not find a version that satisfies the requirement tensorflow ERROR: No matching dis

    ERROR No matching distribution found for xxx的情况这可能是因为网络的问题 这时我们使用国内的镜像源来加速输入命令 python m pip install lxml 如果你安装的是别的库 请输入别
  • Java实现 LeetCode 120 三角形最小路径和

    120 三角形最小路径和 给定一个三角形 找出自顶向下的最小路径和 每一步只能移动到下一行中相邻的结点上 例如 给定三角形 2 3 4 6 5 7 4 1 8 3 自顶向下的最小路径和为 11 即 2 3 5 1 11 说明 如果你可以只使
  • 【YOLOX关键模块记录与解读】YOLOX: Exceeding YOLO Series in 2021

    目录 前言 一 主干部分的Focus网络结构 二 解耦头 三 Mosaic数据增强 四 Anchor Free 五 SimOTA标签匹配策略 六 参考资料 前言 论文地址 https arxiv org abs 2107 08430 源码地
  • uniapp引入外部js链接

    geturl let self this const url https webrtc github io adapter adapter latest js https g alicdn com cloudcallcenter SIPml
  • jenkins安装插件失败

    更改镜像库地址 去以下地址获取当前最新的地址 http mirrors jenkins ci org status html 截取上图红框中的url 在管理插件 gt 高级 中配置成下图中的格式
  • Qt中QLineEdit(取值、赋值、字体大小颜色)、QTextEdit和QPlainTextEdit

    核心 这些东西理解就可以了 下次用的时候看下笔记 能想起来就可以了 背 也背不下来 放弃吧 QString 界面设计最常用到的组件就是QLabel和QLineEdit QLabel用于显示字符串 QLineEdit用于输入和显示字符串 两个
  • 浏览器渲染原理

    浏览器渲染原理 目录 目录 浏览器渲染原理 B S架构及其运行原理 渲染过程 defer async不会阻塞页面解析 B S架构及其运行原理 b s架构是Brower Server结构 chrome浏览器渲染速度最快不同于其他浏览器 因为c
  • Transformer入门(一)——结构

    文章目录 前言 一 Transformer的产生和基础思想 1 1 Transformer的Motivation 1 2 Transformer的原始框架 1 3 关于Decoder的补充说明 二 Encoder中重要模块的具体实现 2 1
  • webpack配置devServer配置https代理与证书

    浏览器如果需要开发某些特定功能 需要https浏览器才会给予功能权限 则开发时我们需要在https环境 下面是配置一个webpack devServer的https环境 webpack config js module exports de
  • C++构造函数/拷贝控制

    1 C 构造函数 C 中构造函数 constructor 的作用是用来控制类对象的初始化过程 构造函数的作用是初始化类对象的数据成员 无论何时只要类的对象被创建 就会执行构造函数 1 1 默认构造函数 当定义一个类对象时 如果没有为对象提供