C++变量初始化形式及其默认初始值问题

目录
  • 什么是初始化
  • 初始化方式
    • 默认初始化
    • 值初始化
    • 直接初始化/拷贝初始化
    • 列表初始化
  • 默认初始值
  • 总结

什么是初始化

当对象在创建时获得了一个特定的值,我们就说这个对象被初始化了。

注意:在C++语言中,初始化和赋值是两个完全不同的操作。

初始化:创建变量时赋予其一个初始值。

赋值:把对象的当前值删除,并赋予一个新的值。

而在很多类中,初始化和赋值的区别事关底层效率问题:前者直接初始化数据成员,后者则先初始化再赋值。

初始化方式

默认初始化

在下面情况发生:

在块作用域中定义非静态变量或者数组时没有赋初值

{
	int var;
	int arr[10];
}

当一个类本身含有类类型的成员且使用合成的默认构造函数时

class B {
	int a = 1;
	int b = 2;
};
class A {
	B m_b;
};

当类类型的成员没有在构造函数初始化列表中显示地初始化时

简单来说,如果在变量初始化时没有指定初始值,则变量进行默认初始化,此时变量被赋予了默认值,默认值到底是什么由变量类型变量的位置决定的,我们后面会具体讲解

值初始化

值初始化是只使用了初始化器(即使用了圆括号或花括号)但却没有提供初始值的情况

int main()
{
	int *p = new int();//值初始化
	vector<int> vec(10);//值初始化
	//int a();错误的初始化方式
	int a = int();//值初始化
	return 0;
}

注意:当不采用动态分配内存的方式(即不采用new运算符)时,写成int a();是错误的值初始化方式,因为这种方式声明了一个函数而不是进行值初始化。如果一定要进行值初始化,必须结合拷贝初始化使用,即写成int a=int();

  • 对于内置类型初始值为0
  • 对于类类型则调用其默认构造函数,如果没有默认构造函数,则不能进行值初始化。
class A {//由于显示声明了构造函数,所以没有默认构造函数
public:
	A(int x) {
		a = x;
	}
	int a;
};

int main()
{
	A object{};//由于没有默认构造函数,初始化出错
	cout << object.a << endl;
	return 0;
}

直接初始化/拷贝初始化

直接初始化与拷贝初始化对应,其内部实现机理不同。

  • 直接初始化在下面情况下发生
  • 采用圆括号的方式进行变量初始化
  • 与值初始化不同,括号里一定要有初始值
  • 用emplace成员创建的元素都进行直接初始化

拷贝初始化在下面情况下发生

  • 采用等号(=)进行初始化
  • 从一个返回类型为非引用类型的函数返回一个对象
  • 用列表初始化一个数组中的元素
int main()
{
	int a(5);//直接初始化
	vector<int>vec1(10);//值初始化
	vector<int>vec2(vec1);//直接初始化
	vector<int>vec3(10,1);//直接初始化
	int b = 10;//拷贝初始化;
	vector<int>vec4 = vec3;//拷贝初始化
}

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

注意:虽然拷贝初始化看起来像是给变量赋值,实际上是执行了初始化操作,与先定义再赋值本质不同。

可以看下面例子:

在这里插入代码片class Foo
{
public:
	Foo() {
		cout << "Foo()" << endl;
	};
	Foo(int n) {
		cout << "Foo(int n)" << endl;
	}
	Foo(const Foo&x) {
		cout << "Foo(const Foo&x)" << endl;
	}
	Foo& operator=(const Foo&x) {
		cout << "Foo& operator=(const Foo&x) " << endl;
		return *this;
	}
	Foo& operator+(const Foo&x) {
		a += x.a;
		cout << "Foo& operator+(const Foo&x) " << endl;
		return *this;
	}
	int a=1;
};
int main()
{
	Foo f1;//默认初始化
	Foo f2 = f1;//拷贝初始化
	Foo f3=f1+f2;//拷贝初始化
	f3 = f2;//赋值操作
}

输出结构:

可以看到在Foo f3=f1+f2;这行代码中并没有执行赋值操作

(1)对于内置类型变量(如int,double,bool等),直接初始化与拷贝初始化差别可以忽略不计。

(2)对于类类型的变量(如string或其他自定义类型),直接初始化调用类的构造函数(调用参数类型最佳匹配的那个),拷贝初始化调用类的拷贝构造函数。

特别的,当对类类型变量进行初始化时,如果类的构造函数采用了explicit修饰而且需要隐式类型转换时,则只能通过直接初始化而不能通过拷贝初始化进行操作。

列表初始化

列表初始化是C++ 11 新引进的初始化方式,它采用一对花括号({})进行初始化操作。而在此之前,比如C++98/03 只有数组和POD类型才可以使用列表初始化。

到了C++ 11,能用直接初始化和拷贝初始化的地方都能用列表初始化,而且列表初始化能对容器进行方便的初始化,所以在新的C++标准中,推荐使用列表初始化的方式进行初始化。

而在某些情况下,初始化的真实含义依赖于初始值时用的是花括号还是圆括号。

代码如下:

int main(void)
{
	vector<int>vec1(10);//vec1有10个元素,每个元素值为0
	vector<int>vec2{10};//vec2有1个元素,值为10
	vector<int>vec3(10, 1); // vec3有10个元素,每个元素值为1
	vector<int>vec4{ 10, 1 };// vec4有2个元素,值分别为10和1
}
  • 如果使用圆括号,提供的值是用来构造对象的
  • 如果使用花括号,表明我们相要列表初始化该对象,即尽可能把花括号中的值当成元素初始值来处理。
  • 但是如果提供的值不能用来列表初始化,则考虑通过构造来进行初始化vector<string>vec5{10};由于花括号里的值与元素类型不同,不能进行列表初始化,所以将vec5有10个元素,每个元素进行默认初始化。

当用于内置类型的变量时,这种初始化形式还有一个重要的特点:如果使用列表初始化且初始值存在丢失信息的风险,则编译器将报错

int main(void)
{
	double d = 1.2345;
	int a(d), b = d;		//正确;虽然编译器不报错,但是会提示存在丢失信息的风险
	int x{ d }, y = { d };//错误;编译器会报错,因为从double转换到int存在丢失信息的风险。
}

默认初始值

内置类型的初始值由定义的位置决定

  • 定义在任何函数体之外的变量被初始化为0
  • 定义在函数体内部的局部变量则未定义,如果试图拷贝或者以其他形式访问该变量,则会引发错误
  • 但是函数体内部的局部静态变量例外,如果局部静态变量没有显示的初始化,它将执行值初始化
int global_a;//值为0
short c;//值为0
int main(void)
{
	static int a;//值为0
	int b;//错误;未进行初始化
}

对于类类型,其默认初始值由类自己决定

  • 如果类中的数据成员在默认构造函数中进行了赋值,则默认初始化值优先使用默认构造函数的值
  • 如果在默认构造函数中没有赋值,但是该数据成员提供了类内初始值,则创建对象时,其默认初始值就是类内初始值,
  • 如果在默认构造函数中没有赋值且没有类内初始值,对于内置类型,则其值未定义,对于类类型,则对其进行默认初始化
class B {
public:
	string str="123";
};
class A {
public:
	A() {
		x = 1;
	}
	int x = 5;
	int y = 10;
	B b;
};
A global_a;
int main(void)
{
	A a;
	cout << global_a.x << " " << global_a.y << " " << global_a.b.str << endl;
	cout << a.x << " " << a.y << " " << a.b.str << endl;
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • C++ 结构体初始化与赋值详解

    目录 1.结构体初始化 2.结构体赋值 参考文献 1.结构体初始化 结构体是常用的自定义构造类型,是一种很常见的数据打包方法.结构体对象的初始化有多种方式,分为顺序初始化.指定初始化.构造函数初始化.假如有如下结构体. struct A { int b; int c; }; (1)顺序初始化因为书写起来较为简约,是我们最常用的初始化方式,但相对于指定初始化,无法变更数据成员初始化顺序,灵活性较差,而且表现形式不直观,不能一眼看出 struct 各个数据成员的值. A a = {1, 2}; (2

  • C++类成员初始化的三种方式

    目录 一.初始化方式 1.初始化方式一:初始化列表 2.初始化方式二:构造函数初始化 3.初始化方式三:声明时初始化(也称就地初始化,c++11后支持) 二.声明时初始化->初始化列表->构造函数初始化 1.声明时初始化的使用场景 2.列表初始化的使用场景 3.构造函数初始化的使用场景 前言: 在C++98中,支持了在类声明中使用等号"="加初始值的方式,来初始化类中静态成员常量.这种声明方式我们也称之为"就地"声明.就地声明在代码编写时非常便利,不过C

  • C++ 成员变量的初始化顺序问题详解

    C++ 成员变量的初始化顺序问题详解 问题来源: 由于面试题中,考官出了一道简单的程序输出结果值的题:如下, class A { private: int n1; int n2; public: A():n2(0),n1(n2+2){} void Print(){ cout << "n1:" << n1 << ", n2: " << n2 <<endl; } }; int main() { A a; a.P

  • C++变量初始化形式及其默认初始值问题

    目录 什么是初始化 初始化方式 默认初始化 值初始化 直接初始化/拷贝初始化 列表初始化 默认初始值 总结 什么是初始化 当对象在创建时获得了一个特定的值,我们就说这个对象被初始化了. 注意:在C++语言中,初始化和赋值是两个完全不同的操作. 初始化:创建变量时赋予其一个初始值. 赋值:把对象的当前值删除,并赋予一个新的值. 而在很多类中,初始化和赋值的区别事关底层效率问题:前者直接初始化数据成员,后者则先初始化再赋值. 初始化方式 默认初始化 在下面情况发生: 在块作用域中定义非静态变量或者数

  • Python的类成员变量默认初始值的坑及解决

    目录 类成员变量默认初始值的坑 问题发现 示例代码 结果 原因 解决方法 Python默认值参数 简单粗暴上代码 可以用下面这种写法 类成员变量默认初始值的坑 问题发现 一个循环内,缺省值初始化同名变量,其中的list成员不是空,会延续之前同名变量的值. 示例代码 # Define class class Variant():     # use     def __init__(self, price = 500, description = 'default description', va

  • 聊聊java变量的初始化之后的默认值

    变量初始化后的默认值 对于类的成员变量 不管程序有没有显示的初始化,Java 虚拟机都会先自动给它初始化为默认值. 1.整数类型(byte.short.int.long)的基本类型变量的默认值为0. 2.单精度浮点型(float)的基本类型变量的默认值为0.0f. 3.双精度浮点型(double)的基本类型变量的默认值为0.0d. 4.字符型(char)的基本类型变量的默认为 "/u0000". 5.布尔性的基本类型变量的默认值为 false. 6.引用类型的变量是默认值为 null.

  • 详解C#中使用对象或集合的初始值设定项初始化的操作

    使用对象初始值设定项初始化对象 可以使用对象初始值设定项以声明方式初始化类型对象,而无需显式调用类型的构造函数. 下面的示例演示如何将对象初始值设定项用于命名对象.编译器通过先访问默认实例构造函数然后处理成员初始化处理对象初始值设定项.因此,如果默认构造函数在类中声明为 private,那么需要公共访问权的对象初始值设定项将失败. 下面的示例演示如何使用对象初始值设定项初始化新的 StudentName 类型. public class Program { public static void

  • Java类中字段可以不赋予初始值的原因分析

    目录 Java类中字段可以不赋予初始值的原因 下面是在Java类中各字段的初始值 Java中类属性的初始化 连接阶段又可以分为三个子步骤:验证.准备和解析 而我们这里所说的主动使用 包括 初始化一个类包括两个步骤 Java中final变量为什么在使用前必须要进行初始化 Java类中字段可以不赋予初始值的原因 Java虚拟机会对类的实例对象进行分配内存,在分配内存后,会将内存空间(除了对象头)全部初始化为零值.这就保证了,在类的定义过程中,不给字段赋初始值,实例对象也能有初始值. 下面是在Java

  • C++中类的构造函数初始值列表解读

    目录 C++类的构造函数初始值列表 1 通过构造函数初始值列表来初始化成员变量 2 通过构造函数的函数体来初始化成员变量 3 类包含常量或引用成员变量 C++类的构造函数的作用 C++类的构造函数初始值列表 1 通过构造函数初始值列表来初始化成员变量 C++中类的构造函数初始值列表可以对类的成员进行初始化. class myClass{   public:   int i;   myClass(): i(1)   {   } }; 其中,类myClass的构造函数中包含了初始值列表,对成员变量i

  • Java未赋值变量的初始值解析(默认值)

    目录 Java未赋值变量的初始值(默认值) 初始值(默认值) 实例变量(非静态字段) 类变量(静态字段) 本地变量 参数 解决java未赋值变量的默认值问题 如下所示 Java未赋值变量的初始值(默认值) 初始值(默认值) 参考:官方文档 Java程序中,任何变量必须初始化后才能使用. 以下为不同数据类型的默认值. 数据类型 初始值 byte 0 short 0 int 0 long 0L char 'u0000' float 0.0f double 0 boolean false 所有引用类型

  • 详解使用angularjs的ng-options时如何设置默认值(初始值)

    这两天我用ng-options过程中遇到的初始值为空白的问题,记得去年就遇到过,怎么解决的忘记了,费了一阵子功夫之后解决了,想记下来,方便遇到同样问题的小伙伴当然还有自己. 1.场景: 就是做一个查询列表的弹窗,其中有一个条件需要用到下拉菜单,数据是用过ajax传递过去的. 2.实现: a.html: <select ng-model="myselect" ng-options="o for o in options"></select> b

  • js判断变量初始化的三种形式及推荐用的形式

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"> //js判断变量初始化有三种形式 var x; if (x == null) { alert("x为null"); } if (typeof (x) == "undefined&qu

  • select2 ajax 设置默认值,初始值的方法

    在做功能的时候需要修改数据, 修改数据时需要显示原始值. 但是在select2的时候 显示原始值是一个非常非常非常非常非常要命的难题. 研究了3个小时, 最后使用$.ajax 重新加载原始值, 并显示. //two AJAX获取数据方式(每次请求) var $c_HospitalCode = $("#c_HospitalCode").select2({ ajax: { type: 'GET', url: "/Report/AjaxOption/Ajax_LoadHospita

随机推荐