初始化、赋值、默认初始化、列表初始化、类内初始值、直接初始化与拷贝初始化

文章目录

  • 初始化和赋值的区别
  • 什么是默认初始化?
  • 列表初始化
    • 列表初始化的使用场景
    • 不适合使用列表初始化的场景
  • 类内初始值
  • 混用string对象和C风格字符串
  • 数组与vector对象
    • 关于vector对象
    • 两者间的初始化关系
  • 直接初始化与拷贝初始化


初始化和赋值的区别

  • 初始化的含义是创建变量时赋予其一个初始值
  • 赋值的含义时把对象的当前值擦除,而已一个新值来替代。


什么是默认初始化?

如果定义变量时没有指定初值,则变量被默认初始化,此时变量被赋予了 “默认值”。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。

内置类型的默认值由定义的位置决定, 定义于任何函数体之外的变量被初始化为0;定义于函数体内部的内置类型将不被初始化一个未被初始化的内置类型变量的值是未定义的,试图拷贝或以其他形式访问此类值将引发错误。



列表初始化

C++定义了初始化的好几种不同形式,通常定义一个变量并初始化的方式有以下四种:

int x = 0;
int x = {0};
int x{0};
int x(0);

使用花括号来初始化变量在C++11新标准中得到了全面应用。这种初始化的形式被程为列表初始化(list initialization)。现在,无论是初始化对象,还是某些时候为对象赋新值,都可以使用列表初始化。

列表初始化的使用场景

  1. 列表初始化可被用于以下场景:
// Vector 接收了一个初始化列表。
vector<string> v{"foo", "bar"};// 不考虑细节上的微妙差别,大致上相同。
vector<string> v = {"foo", "bar"};// 可以配合 new 一起用。
auto p = new vector<string>{"foo", "bar"};// map 接收了一些 pair, 列表初始化大显神威。
map<int, string> m = {{1, "one"}, {2, "2"}};// 初始化列表也可以用在返回类型上的隐式转换。
vector<int> test_function() { return {1, 2, 3}; }// 初始化列表可迭代。
for (int i : {-1, -2, -3}) {}// 在函数调用里用列表初始化。
void TestFunction2(vector<int> v) {}
TestFunction2({1, 2, 3});
  1. 用户自定义类型也可以定义接收 std::initializer_list<T> 的构造函数和赋值运算符,以自动列表初始化:
class MyType {public:// std::initializer_list 专门接收 init 列表。MyType(std::initializer_list<int> init_list) {for (int i : init_list) append(i);}MyType& operator=(std::initializer_list<int> init_list) {clear();for (int i : init_list) append(i);}
};
MyType m{2, 3, 5, 7};
  1. 最后,列表初始化也适用于常规数据类型的构造,哪怕没有接收 std::initializer_list<T> 的构造函数。
// MyOtherType 没有 std::initializer_list 构造函数,
// 直接上接收常规类型的构造函数。
class MyOtherType {public:explicit MyOtherType(string);MyOtherType(int, string);
};
MyOtherType m = {1, "b"};
// 不过如果构造函数是显式的(explict),就不能用 `= {}` 了。
MyOtherType m{"b"};

不适合使用列表初始化的场景

值得注意的是,当用于内置类型的变量时,如果使用列表初始化且初始值存在丢失信息的风险,则编译器将报错:

long double ld = 3.1415926536;
int a{ld}, b = {ld}; // 错误:转换未执行,因为存在丢失信息的危险
int c(ld), d = ld; // 正确:转换执行,且确实丢失了部分值

使用 long double 的值初始化 int 变量时可能丢失数据,所以编译器拒绝了 ab 的初始化请求。其中,至少 ld 的小数部分会丢失掉,而且某些情况下 int 也可能存不下 ld 的整数部分。

同时,千万别直接列表初始化 auto 变量,因为可读性不高:

auto d = {1.23};        // d 类型是 std::initializer_list<double>
auto d = double{1.23};  // d 类型为 double, 并非 std::initializer_list.


类内初始值

C++11标准规定,可以为数据成员提供一个类内初始值(in-class initializer)。创建对象时,类内初始值将用于初始化数据成员。没有初始值的成员将被默认初始化。

对类内初始值的限制如下:

  1. 放在花括号里
  2. 放在等号右边
  3. 不能使用圆括号

因为我们无法避免这样的情况,有时函数声明也会用到圆括号:

class Widget 
{
private: typedef int x;int z(x);
};

因此用圆括号为类内成员提供类内初始值容易产生二义性,编译器会觉得该语句语义不明。



混用string对象和C风格字符串

我们都知道允许使用字符串字面值来初始化string对象:

string s("Hello World!");

C++规定,任何出现字符串字面值的地方都可以用以空字符结束的字符数组来替代

  • 允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值。
  • 在string对象的加法运算中允许使用以空字符结束的字符数组作为其中一个运算对象(不能两个对象都是);在string对象的复合赋值运算中允许是用以空字符结束的字符数组作为右侧的运算对象

上述性质反过来并不成立:如果程序的某处需要一个C风格字符串,无法直接用string对象来替代它。

例如:不能使用string对象直接初始化指向字符的指针。为了实现这一功能,string专门提供了一个名为c_str的成员函数:

char *str = s; // 错误:不能用string对象初始化char*
const char *str = s.c_str; // 正确

函数返回结果使用一个指针,该指针指向一个以空字符结束的字符数组,而这个数组所存的数据恰好与哪个string对象的一样。结果指针的类型是const char*,从而确保我们不会改变字符数组的内容。

PS:由于我们无法保证c_str函数返回的数组一直有效,如果后续的操作改变了s的值就可能让之前返回的数组失去效用。因此,如果执行完c_str()函数后程序想一直都能使用其返回的数组,最好将该数组重新拷贝一份。



数组与vector对象

关于vector对象

vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型,如:

vector<int>

两者间的初始化关系

  1. 不允许使用一个数组为另一个内置类型的数组赋初值
  2. 不允许使用vector对象初始化数组
  3. 允许使用数组来初始化vector对象

实现第三点只需要指明要拷贝区域的首元素地址和尾后地址就可以了:

int int_arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(begin(int_arr), end(int_arr));

用于创建ivec 的两个指针实际上指明了用来初始化的值在数组int_arr中的位置,分别用标准库函数begin和end来计算int_arr的首指针和尾后指针。在最终结果中,ivec将包含6个元素,它们的次序和值都与数组int_arr完全一样。

亦可使用数组的一部分来初始化vector对象:

vector<int> subVec(int_arr + 1, int_arr + 4);
// 拷贝三个元素:int_arr[1]、int_arr[2]、int_arr[3]


直接初始化与拷贝初始化

在这里插入图片描述

  • 使用直接初始化时,编译器进行函数匹配来选择与我们提供的参数最匹配的构造函数。
  • 使用拷贝初始化时,编译器将右侧运算对象拷贝到正在创建的对象中,按需选择是否进行类型转换。

拷贝初始化通常使用拷贝构造函数来完成。

拷贝初始化不仅在我们用 = 定义变量时会发生,也会在下列情况中发生:

  1. 将一个对象作为实参传递给一个非引用类型的形参
  2. 从一个返回类型为非引用类型的函数返回一个对象
  3. 用花括号列表初始化一个数组中的元素或一个聚合类中的成员

拷贝初始化受到explicit类型的构造函数的限制:

需要类型转化的拷贝初始化的过程是这样的:

  1. 隐式调用给定类型的构造函数,将等号右边的值作为实参传递给构造函数,从而生成一个临时的给定类型的对象。(类型转换通过本步完成,如果是无需类型转换的拷贝初始化则没有本步)
  2. 再让想要初始化的对象隐式调用拷贝构造函数,并将临时对象作为实参传给拷贝构造函数,从而完成初始化。

这样的操作对于explicit类型的构造函数是行不通的,因为无法隐式调用一个explicit的构造函数生成一个临时对象。 换言之,explicit类型的构造函数是抑制隐式类型转换的。

因此面对explicit类型的构造函数只能执行直接初始化、或者是无需类型转换的拷贝初始化:

// error:vector接受大小参数的构造函数是explicit的
vector<int> vi = 10; vector<int> vi(10); // 正确:直接初始化string s(10, 'x'); // 正确:直接初始化
string s1 = s; // 正确:无需类型转换的拷贝初始化

如果希望使用有给explicit的构造函数,必须显式地使用:

void f(vector<int>); // f的参数进行拷贝初始化
f(10); 
// error:不能隐式地使用explicit的构造函数构造一个临时vector
// 因为无法执行从int(也就是10的类型)到vector的类型转换
f(vector<int>(10)); 
// 正确:显式地使用explicit的构造函数
// 为vectoc的构造函数传入10作为实参,构造一个临时的vector
// 用临时的vector初始化f

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443836.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js动态增加,删除td,tr,table,div

js实现的动态添加&#xff0c;删除table内容&#xff1a; 截图如下&#xff1a; 1. 2. 源代码&#xff1a; main.css body {background-image: url(../images/qiantai/bg.png);font-family: arial;font-size: 12px;color: #d4d7da;text-align: center;background-repeat: r…

jQuery实现复选框的全选和反选:

jQuery实现复选框的全选和反选&#xff1a; 截图如下&#xff1a; 代码如下&#xff1a; index.jsp: <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%String path request.getContextPath();String basePath…

C语言隐式/显式类型转换 | C++四种强制类型转换、类的隐式转换、explicit

文章目录C语言类型转换隐式类型转换显式类型转换C 强制类型转换static_castreinterpret_castconst_castdynamic_cast类的隐式类型转换概念只允许一步类类型转换explicit 抑制构造函数定义地隐式转换可以通过显式转换使用explicit构造函数C语言类型转换 隐式类型转换 编译器在…

函数重载、引用再探、内联函数

文章目录函数重载为什么C支持重载&#xff0c;C语言不支持呢&#xff1f;extern “C”引用再探引用的特性引用的使用场景引用和指针引用和指针的不同点:内联函数什么是内联函数&#xff1f;内联函数的特性内联函数的好处类的内联成员函数的声明内联函数的使用constexpr函数概念…

类的概念、成员函数的定义方式、类的访问控制和封装、类的大小、this指针

文章目录类的概念structclassclass和struct的区别是什么呢&#xff1f;类中成员函数的两种定义方式声明和定义都在类中声明和定义分离类的访问控制和封装类的封装特性类的大小结构体内存对齐规则类的存储方式this指针类的概念 在C中&#xff0c;类可以说是最重要的东西&#x…

jQuery实现两个列表框的值之间的互换:

jQuery实现两个列表框的值之间的互换&#xff1a; 截图如下&#xff1a; 代码如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%String path request.getContextPath();String basePath reque…

类的6个默认成员函数:构造函数、析构函数、拷贝构造函数、重载运算符、三/五法则

文章目录6个默认成员函数构造函数概念默认构造函数的类型默认实参概念默认实参的使用默认实参声明全局变量作为默认实参某些类不能依赖于编译器合成的默认构造函数第一个原因第二个原因第三个原因构造函数初始化构造函数里面的“”是初始化吗&#xff1f;为什么要使用列表初始化…

C++ 类的知识 | 构造函数再探、匿名对象、友元函数、内部类、类的const成员、类的static成员

文章目录构造函数再探以下代码共调用多少次拷贝构造函数委托构造函数概念形式匿名对象友元友元的声明友元类令成员函数作为友元函数重载和友元注意内部类特性类的const成员可变数据成员类的static成员概念关于static静态成员的类内初始化静态成员能用于某些普通成员不能的场景构…

顺序容器(vector、list、string、deque、forward_list)及迭代器、容器适配器

文章目录概述所有容器都支持的操作迭代器迭代器支持的操作迭代器支持的算术运算容器类型size_typeiterator 和 const_iterator容器定义和初始化拷贝初始化顺序容器独有的构造函数&#xff08;array除外&#xff09;array的初始化与内置数组类型的区别6种初始化方法&#xff08;…

jQuery实现表格隔行换颜色:

jQuery实现表格各行换颜色&#xff1a; 截图如下&#xff1a; 代码如下&#xff1a; <span style"font-family:Microsoft YaHei;font-size:14px;"><% page language"java" import"java.util.*" pageEncoding"UTF-8"%> &…

用stack处理中缀表达式【+、-、*、/、()】

文章目录题目描述思路使用getline()存储输入的字符串边读取边压栈完整代码题目描述 使用stack处理括号化的表达式。当你看到一个左括号&#xff0c;将其记录下来。当你在一个左括号之后看到一个右括号&#xff0c;从stack中pop对象&#xff0c;直至遇到左括号&#xff0c;将左括…

二维数组的查找

文章目录题目描述思路注意代码题目描述 在一个 n * m 的二维数组中&#xff0c;每一行都按照从左到右递增的顺序排序&#xff0c;每一列都按照从上到下递增的顺序排序。请完成一个高效的函数&#xff0c;输入这样的一个二维数组和一个整数&#xff0c;判断数组中是否含有该整数…

Springmvc,Spring MVC文件上传

Springmvc文件上传&#xff1a; 1.代码截图如下&#xff1a; 2.UploadController.java: package cn.csdn.controller;import java.io.File;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller; import org.springframework.ui.…

插入迭代器、流迭代器、反向迭代器、移动迭代器

文章目录前言插入迭代器inserterfront_inserterback_inserteriostream迭代器istream_iterator 读取输入流istream_iterator允许使用懒惰求值ostream_iterator操作反向迭代器reverse_iterator的base成员函数前言 除了为每个容器定义的迭代器之外&#xff0c;标准库在头文件iter…

泛型算法(lambda表达式、function类模板、bind函数适配器、迭代器类别、链表数据结构独有的算法)

文章目录概念find()函数迭代器令算法不依赖于容器但算法依赖于元素类型的操作算法永远不会执行容器的操作只读算法accumulate()函数从两个序列中读取元素&#xff08;equal函数为例&#xff09;迭代器作为参数形成两个序列equal()写容器元素的算法概念fill()fill_n()插入迭代器…

jsp,div 限制字数,超出部分用省略号代替

1.我是用struts2标签做的&#xff1a;如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <% taglib prefix"s" uri"/struts-tags"%> <%String path request.getContext…

C++之关联容器

文章目录概述及类型mapsetpair类型概念初始化默认初始化提供初始化器允许的操作可以创建一个pair类的函数可以作为容器的类型关联容器迭代器概念map的迭代器set的迭代器是const的初始化map and setmultimap and multiset关联容器的操作额外的类型别名关联容器和算法删除元素添加…

动态内存、智能指针(shared_ptr、unique_ptr、weak_ptr)、动态数组

文章目录三种对象的分类三种内存的区别动态内存概念智能指针允许的操作智能指针的使用规范new概念内存耗尽/定位new初始化默认初始化直接初始化值初始化delete概念手动释放动态对象空悬指针shared_ptr类格式独有的操作make_shared函数shared_ptr的计数器通过new用普通指针初始化…

动态数组的简单应用

文章目录连接两个字符串字面常量题目注意代码输出结果处理输入的变长字符串题目注意代码连接两个字符串字面常量 题目 连接两个字符串字面常量&#xff0c;将结果保存在一个动态分配的char数组中。重写&#xff0c;连接两个标准库string对象。 注意 使用头文件cstring的str…

二分查找算法实现

文章目录思路代码以二分区间作为while判定条件以给定值作为while判定条件主函数思路 while判定条件的选择&#xff0c;注意最外层的return的内容就好。下面提供了两个代码版本。计算 mid 时需要防止溢出&#xff08;对应类型【如本例中的int】可能存不下&#xff09;&#xff…