2018-07-08 19:12:58 maomaolaoshi 阅读数 1668
  • 征服C++ 11视频精讲

    扫码进入学习群,领取学习资料+讲师答疑 本教程是C++ Primer Plus 6th 、The C++ Programming Language_4th、Thinking in C++等国外顶级的C++著作的精华和个人超过10年的C++使用经验的完美结合。

    78368 人正在学习 去看看 李宁

C++初始化变量

C++初始化变量一共有四种方式

等于号,’=’初始化

int a = 0;

在等号的右边输入初始值。

括号,’()’初始化

int a(0);

在括号的中间输入初始值。

花括号+等于号,’{}’+’=’初始化,又称为初始化列表

int a={0};

在等号右边的花括号里面输入值。

花括号,’{}’声明,又称为初始化列表

int a{0};

在花括号里面输入值。

注意事项:

初始化列表这种方式是不允许有数据丢失。比如使用一个浮点类型的值去初始化一个整型的变量,系统就会报错。
例如:
使用double类型的值初始化int变量,就会造成数据丢失。double小数部分的值肯定会数据丢失,整数部分的值可能会丢失(如果double的整型部分超出了int的最大长度。)。

2017-03-18 16:18:18 u014182411 阅读数 539
  • 征服C++ 11视频精讲

    扫码进入学习群,领取学习资料+讲师答疑 本教程是C++ Primer Plus 6th 、The C++ Programming Language_4th、Thinking in C++等国外顶级的C++著作的精华和个人超过10年的C++使用经验的完美结合。

    78368 人正在学习 去看看 李宁

一 、c++ 几种初始化方式:

什么是初始化?

在创建变量时,给变量一个初始的值。

什么是赋值?

把变量当前的值擦除,赋予一个新的值。


1. 默认初始化

在定义变量时,没有指定这个变量的初始值,此时变量被默认初始化。变量被默认初始化的值是什么呢?

这取决与变量定义的位置和变量的类型。

(a) 对于在任何函数之外(包括main函数)的内置类型变量(整数、浮点数、单个字符和布尔值),默认初始化值0。

(b)对于在函数体之内的内置类型变量,不会进行默认初始化。未初始化的值有一个未知的值,这是因为为变量分配的存储空间里面之前存储的值并没有被清空。若直接引用或运算将引发不可预知的错误。而此错误编译器可能无法检测,而导致程序以错误的方式运行。如下:

int main() {

    int result;
    for (int i = 0; i <= 10; i++ )
        result += i ;
    cout << "result = " << result << endl;
    return 0;
}

运行结果:

result = 4201074

所以最好的方式就是对于函数体内的内置类型一定要显示初始化。

(b) 对于类类型,类自己决定是否提供默认初始化,若提供默认初始化,类自己决定默认值。


2、拷贝初始化

直接使用等号或圆括号初始化。

int i = 0
int result(0);

3、列表初始化

在c++11的标准中可采用列表初始化。

int m = {0};
int h{0};

二、vector类型的初始化问题

1、vector是标准库中的一种标准库模板。vector的初始化方式有下面几种:

int n = 10, val = 7;
/*一个空的vector,元素类型是int*/
 vector <int> v1;
/*v2v3中包含v2中所有元素的值*/
 vector <int> v2 = v1;
 vector <int> v3(v1);
/*包含n个元素,每个元素的值为val*/
 vector <int> v4(n, val);
/*n个元素的值都是初始化的值*/
 vector <int> v5(n);
/*每个元素被赋予相应的初始值,表达等价*/
 vector <int> v6 = {1,2,3,4};
 vector <int> v7{1,2,3,4};
 

如果没有提供初始化的值,会根据vector的类型来进行默认初始化。int类型默认为0,string类型为空。

2、关于使用圆括号与花括号的区别:

v1有10个元素,每个为0。

vector <int> v1(10);
v1只有一个元素,为10。

vector <int> v1{10};
v1有10个元素,每个为1。

vector <int> v1(10,1);
v1有两个元素,一个是10,一个是1。

vector <int> v1{10,1};

如果是圆括号,只有一个整数参数,则是指明vector的容量。有两个参数,第一个为整数,第二个为vector元素的类型,则指明存储了多少个重复的元素。

如果是花括号,则用花括号中的元素来初始vector元素。但是当花括号中的值不能用于初始化vetctor中的元素时,花括号的作用与圆括号一致。如下:

10不能初始化string类型的元素,所以此时表示的是v1中有10个元素,每个元素为空的string

vector <string> v1{10};
同样,下面表示的是v2中有10个“hi”元素。

vector <string> v2{10,"hi"};

2019-08-25 13:18:51 weixin_43946347 阅读数 22
  • 征服C++ 11视频精讲

    扫码进入学习群,领取学习资料+讲师答疑 本教程是C++ Primer Plus 6th 、The C++ Programming Language_4th、Thinking in C++等国外顶级的C++著作的精华和个人超过10年的C++使用经验的完美结合。

    78368 人正在学习 去看看 李宁

C++的初始化有很多方式:默认初始化,值初始化,直接初始化,拷贝初始化,列表初始化。这些方式之间有什么区别与联系呢?我们一一来看。

默认初始化

默认初始化是指定义变量时没有指定初值时进行的初始化操作。例如int a; Sales_data myData;等等。这些变量被定义了而不是仅仅被声明(因为没有extern关键字修饰),而且没有显式的赋予初值。特别的,如果采用动态分配内存的方式(即采用new关键字)创建的变量,不加括号时(如int *p=new int;)也是默认初始化,加了括号(如int *p=new int())为值初始化。变量的值与变量的类型与定义的位置有关系。

(1)对于内置类型变量(如int,double,bool等),如果定义在语句块外(即{}外),则变量被默认初始化为0;如果定义在语句块内(即{}内),变量将拥有未定义的值。

(2)对于类类型的变量(如string或其他自定义类型),不管定义于何处,都会执行默认构造函数。如果该类没有默认构造函数,则会引发错误。因此,建议为每个类都定义一个默认构造函数。

int a;
Sales_data myData;
vector<string> svec;
string s;

值初始化

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

int *p=new int();
// 对于vector而言,只提供了vector对象容纳的元素数量而略去初始值
vector<string> svec(10);
vector<int>ivec(10);

等等都是典型的值初始化方式。注意,当不采用动态分配内存的方式(即不采用new运算符)时,写成int a();是错误的值初始化方式,因为这种方式声明了一个函数而不是进行值初始化。如果一定要进行值初始化,必须结合拷贝初始化使用,即写成int a=int();值初始化和默认初始化一样,对于内置类型初始化为0,对于类类型则调用其默认构造函数,如果没有默认构造函数,则不能进行初始化。

直接初始化与拷贝初始化

直接初始化与拷贝初始化对应,其内部实现机理不同
直接初始化是指采用小括号的方式进行变量初始化(小括号里一定要有初始值,如果没提供初始值,那就是值初始化了!)。

int a(12);
Sales_data myData(para);
vector<int> ivec(ivec2);//直接初始化对于vector容器而言,仅支持这样的形式
string s("123456");

注意 vector<int> ivec(10)是值初始化,并不是直接初始化。
拷贝初始化是指采用等号(=)进行初始化的方式。

int a=12;
Sales_data myData=para;
vector<int> ivec=ivec2;
string s=string("123456");

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

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

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

直接初始化不一定要调用拷贝构造函数,而拷贝初始化一定要调用拷贝构造函数。特别的,当对类类型变量进行初始化时,如果类的构造函数采用了explicit修饰而且需要隐式类型转换时,则只能通过直接初始化而不能通过拷贝初始化进行操作。关于详细介绍参考 C++的一大误区——深入解释直接初始化与复制初始化的区别

列表初始化

列表初始化是C++ 11 新引进的初始化方式,它采用一对花括号(即{})进行初始化操作。能用直接初始化和拷贝初始化的地方都能用列表初始化,而且列表初始化能对容器进行方便的初始化,因此在新的C++标准中,推荐使用列表初始化的方式进行初始化。

int a{12};
string s{"123"};
//对vector容器而言
vector<int> vec{1,2,3};
vector<string> v1{"a","an","the"};

这里一定要注意,列表初始化使用的是花括号而不是圆括号!

创建指定数量的元素
还可以用vector对象容纳的元素数量和所有元素的统一初始值来初始化vector对象:

vector<int> ivec(10,-1);//10个int类型的对象,每个对象都被初始化为-1
vector<string> svec(10,"hi!");//10个string对象,每个都是"hi!"

通常通过花括号或圆括号来区分初始化含义

vector<int> v1(10);//v1有10个元素,每个都为0
vector<int> v2{10};//v2有1个元素,为10
vector<int> v3(10,1);//v3有10个元素,每个都是1
vector<int> v4{10,1};//v4有2个元素,值分别是10和1

另一方面,如果初始化使用了花括号的形式但是提供的值又不能用来列表初始化

vector<string> v5{"hi"};//列表初始化,v5有1个元素
vector<string> v6("hi");//错误,不能用字符串字面值来构建vector对象,即没有提供数量
vector<string> v7{10};//提供的值补鞥能用来列表初始化,所以是值初始化,v7有10个默认初始化的元素
vector<string> v8{10,"hi"};//不能列表初始化,v8有10个值为"hi”的元素

但一般使用vector都是默认初始化后,再用成员函数push_back添加元素。

————————————————
参考
原文链接:https://blog.csdn.net/u014359097/article/details/50788911

2013-04-28 00:39:15 mlkiller 阅读数 933
  • 征服C++ 11视频精讲

    扫码进入学习群,领取学习资料+讲师答疑 本教程是C++ Primer Plus 6th 、The C++ Programming Language_4th、Thinking in C++等国外顶级的C++著作的精华和个人超过10年的C++使用经验的完美结合。

    78368 人正在学习 去看看 李宁

前言

如果你做过c/c++的开发,如果你参加过c/c++的面试,那么初始化这个问题无疑是必考的题目。
开始的时候,我也很奇怪,初始化这个东西有什么好讲的,简单的说不就是没有给初值么,等看到很多错误,调试很多代码,吃了亏,上了当之后,才明白别人再怎么强调初始化的重要性都不为过。
java这点很好,必须初始化,否则编译不过。正向有个老师说的java就像是一个被保护好的孩子,你一旦有点问题就阻止你,但是c/c++就不是了,给你自由,出问题你自己负责。

一内置变量初始化

1 基本变量

这里主要讲内置变量int, float ,long ,double,bool等
首先说初始化的概念:
在定义变量的时候,给变量赋予初值,叫做初始化的变量,否则就叫未初始化的变量。

没有被初始化的变量他们的值是什么?
如果是局部变量,则是随机值,如果是全局变量则为0.
未初始化只有一个好处,如果初始化变量位于代码段中,会占用一定的空间。但是未初始化的坏处几乎是全部。
个人建议,如果你没有pclint等检查工具检查你的代码,最好给局部变量赋予初值,防止出现一些莫名其妙的错误,查问题耽误太多时间。
(ps,pclint检测的时候,如果你初始化了,但是却没有用这个变量,则会报错)
举个例子:
#include<iostream>
using namespace std;

int main()
{
   int sum;  
   for (int i =0 ;i<10;i++)
         sum += i;
    cout<<sum<<endl;
}

我电脑里面运行的结果是2281105。而不是你想要的55.
你可能会说这个很简单,但是现实中编程序,开始有一堆的变量,我记不太清,c89的时候c语言必须所有的变量都在开头的时候声明,这样会导致,你声明了一堆变量,有些你要初始化,有些你不初始化,就会很麻烦
关键在于很多crash由于没有初始化导致的,比较麻烦的事情是,由于是随机值,有些环境是ok,有些环境出问题,而且还不是必现的,查问题相当困难。


另外讲一下 c++ primer里面比较蛋疼的一句话。
“初始化不是赋值”。
初始化有两种情况:
int a = 1;     //赋值初始化
int a(1);     // 直接初始化
赋值是什么?
a = 2;
你可以理解为定义a的时候的=号和其他时候的=不一样。这个在复杂类里面才能看出来区别,这里看不出来。
直接初始化比赋值初始化效率高一些。

2 常量

常量必须初始化,因为常量不能被修改。
const int a  = 5; // ok
int b;// 错误,没初始化
a = 3;//错误,被修改

这里加一句,书上看到的,虽然和初始化没关系。就是有const限定符的函数外定义的变量,默认是只能在这个文件中访问的,别的文件中访问需要加extern.
a.cpp里面
const int a =3;
int b;     // 相当于extern int  b =0;
const int c =4;// 如果b文件中用到,需要声明为 extern const int c = 4;
b.cpp 里面
int b;//编译的时候会报错,重复定义,修改为extern
const int a = 3;// ok
extern const int c;//错误,该声明没有定义。
(ps 我会在后面的声明和定义里面讲一下这个内容)

3 引用

引用必须初始化,引用指向的对象不能变,但是对象的内容可以变。
int a = 3;
int b = 4;
int &c  =a;
int &d;// 编译会报错,引用必须初始化,
c = b;
这个时候a,b,c的值是多少呢?都是4。
c = 5;
这个时候呢?a = 5, b= 4,c =5;
所以上面的那句c = b;相当于c = 4,也就是a =4;后面对c的所有操作都是改变a的值。c不会指向其他对象了。



4枚举
枚举是常量
enum test
{
a,
b,
c
};
这个时候a = 0, b = 1 ,c =2, 枚举成员的第一个默认初始化为0,
enum test
{
c=1,
d,
e=5,
f
};
这个时候d =2, f =6,没有初始化的默认为它前面的那个值+1;

5 数组

int a[5];//这些值都是随机值
int b[5] ={1,2,3,4,5};//全部都初始化了
int c[5]={1,2};//相当于{1,2,0,0,0}
int d[100] ={0};//相当于将数组全部初始化为0

6 字符串
字符串是c语言的特性,它是一种特殊的产物;举个例子
char *p = "abcd";
这个和
char p[]="abcd";
char p[5]={'a','b','c','d','\0'};
效果是一模一样的,char *p = "abcd";这句话很特殊,编译的时候会报警告,说不建议这么写,但是实际上这么写也没有问题。

int a =5;
int *b =&a;
char p = 'a';
char *q = &p;

你会发现如果用printf打印,是没有问题的,因为你已经指定好了类型。但是如果用cout;
cout<<b<<endl;
cout<<q<<endl;

这个时候b打印出来会是个地址,而q这个时候,cout就会把它当成一个字符串,就会打印直到遇到'\0',而这个时候输出的结果就要看a的值,你可以试试改变a的值,则每次打印的结果不一样。

7 指针

指针没有初始化,就是野指针,指向一个随机的地址,破坏性很大。指针一定要初始化。

二类的初始化


1 类成员变量初始化

可以参考这篇文章写得很详细http://blog.csdn.net/jenghau/article/details/4752735
类成员的初始化,不能再定义的时候初始化,要在构造函数里面初始化。
class A{
public:
     A();
private:
      int data;
      static int st_data;
      const int const_data = 10;
};
简单总结一下:
1.1普通变量,两种初始化方式:
第一,初始化列表
A::A():data(0)
{
}
第二,赋值
A:A()
{
    data = 0;
}
如果不初始化,则遵守上面的规则,内置类型随机值,类的话调用构造函数。
1.2 静态变量

int A::st_data = 0;//类外初始化
它是所有类共享的,和对象没有关系

1.3 const 常量
创建的时候初始化,见上面的例子
1.4 引用变量
在初始化列表里面初始化
class B{
public:
   B();
   B(A a);
private:
    A &m_a;
}

B::B(A a):m_a(&a)
{
}

5 const static的
 short 类型的在类定义初始化
 float在构造函数中初始化。

2 类对象初始化

局部对象没有初始化,它的值是多少,开始我想这个问题的时候,总和上面的内置类型的变量弄混淆,如果是自定义的类对象变量,它的值一般为空。
例如
string a;//输出之后a就是空的。
为什么呢?和上面的似乎有些矛盾,其实并不矛盾。上面的是基本类型。
类呢,本身有个默认构造函数,它会初始化对象。
受上面的例子的毒害,我一直认为,模式构造函数如果不声明,它会将类中的元素给初始化为0或者空值,但是实际情况并非如此,看下面的例子。
 
#include<iostream>
#include<string>
using namespace std;

class A
{
public:
   int data;
friend ostream& operator<<(ostream&,const A&);       //同理
};

ostream& operator<<(ostream&,const A& a)
{
    cout<<a.data<<endl;
}

int main()
{
  A a;
  cout<<a.data<<endl;
  cout<<a;

}

我这里的输出是
2281060
2281060
不过和编译有关系,有的时候也能碰巧为0,怎么验证这个值是随机值呢,你改变一下代码的顺序,或者在class A里面随便加几个不用的变量等等,就会发现输出结果发生变化了。

但是如果我们自己写构造函数,必须先在类里面声明一下。
A::A()
{
   data = 0;  
}  
那么上面的结果就一定是0了。
可见string 的默认构造函数在标准库就将里面的char* 初始化为了0
2013-11-18 00:21:21 u012786754 阅读数 619
  • 征服C++ 11视频精讲

    扫码进入学习群,领取学习资料+讲师答疑 本教程是C++ Primer Plus 6th 、The C++ Programming Language_4th、Thinking in C++等国外顶级的C++著作的精华和个人超过10年的C++使用经验的完美结合。

    78368 人正在学习 去看看 李宁

C++ 初始化列表

何谓初始化列表

与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。在C++中,struct和class的唯一区别是默认的克访问性不同,而这里我们不考虑访问性的问题,所以下面的代码都以struct来演示。

struct foo
{
    string name ;
    int id ;
    foo(string s, int i):name(s), id(i){} ; // 初始化列表
};

构造函数的两个执行阶段

构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。

初始化阶段

所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。

计算阶段

一般用于执行构造函数体内的赋值操作,下面的代码定义两个结构体,其中Test1有构造函数,拷贝构造函数及赋值运算符,为的是方便查看结果。Test2是个测试类,它以Test1的对象为成员,我们看一下Test2的构造函数是怎么样执行的。

复制代码
struct Test1
{
    Test1() // 无参构造函数
    { 
        cout << "Construct Test1" << endl ;
    }

    Test1(const Test1& t1) // 拷贝构造函数
    {
        cout << "Copy constructor for Test1" << endl ;
        this->a = t1.a ;
    }

    Test1& operator = (const Test1& t1) // 赋值运算符
    {
        cout << "assignment for Test1" << endl ;
        this->a = t1.a ;
        return *this;
    }

    int a ;
};

struct Test2
{
    Test1 test1 ;
    Test2(Test1 &t1)
    {
        test1 = t1 ;
    }
};
复制代码

调用代码

Test1 t1 ;
//调用构造函数系统就是提前把类里面的变量提前初始化,会调用变量的默认构造函数,这里相当于t1()
Test2 t2(t1) ;//这个也是初始化

输出

解释一下,第一行输出对应调用代码中第一行,构造一个Test1对象。第二行输出对应Test2构造函数中的代码,用默认的构造函数初始化对象test1,这就是所谓的初始化阶段。第三行输出对应Test1的赋值运算符,对test1执行赋值操作,这就是所谓的计算阶段。

为什么使用初始化列表

初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。使用初始化列表主要是基于性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由上面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。同样看上面的例子,我们使用初始化列表来实现Test2的构造函数

struct Test2
{
    Test1 test1 ;
    Test2(Test1 &t1):test1(t1){}//这个就是告诉编译器不用初始化了 已经初始化了 少调用一次默认构造函数
}

使用同样的调用代码,输出结果如下。

第一行输出对应 调用代码的第一行。第二行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表。

哪些东西必须放在初始化列表中

除了性能问题之外,有些时场合初始化列表是不可或缺的,以下几种情况时必须使用初始化列表

  • 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
  • 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
  • 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

对于没有默认构造函数的类,我们看一个例子。

复制代码
struct Test1
{
    Test1(int a):i(a){}
    int i ;
};

struct Test2
{
    Test1 test1 ;
    Test2(Test1 &t1)
    {
        test1 = t1 ;
    }
};//调用构造函数系统就是提前把类里面的变量提前初始化,会调用变量的默认构造函数,如果main中调用Test1 t1;这里会报错 因为没有默认构造函数
	Test2 t2(t1);
复制代码

以上代码无法通过编译,因为Test2的构造函数中test1 = t1这一行实际上分成两步执行。

1. 调用Test1的默认构造函数来初始化test1

2. 调用Test1的赋值运算符给test1赋值

但是由于Test1没有默认的构造函数,所谓第一步无法执行,故而编译错误。正确的代码如下,使用初始化列表代替赋值操作。

struct Test2
{
    Test1 test1 ;
    Test2(Test1 &t1):test1(t1){}
}

成员变量的初始化顺序

成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的,看代码。

struct foo
{
    int i ;
    int j ;
    foo(int x):i(x), j(i){}; // ok, 先初始化i,后初始化j
};

再看下面的代码

struct foo
{
    int i ;
    int j ;
    foo(int x):j(x), i(j){} // i值未定义
};

这里i的值是未定义的因为虽然j在初始化列表里面出现在i前面,但是i先于j定义,所以先初始化i,但i由j初始化,此时j尚未初始化,所以导致i的值未定义。所以,一个好的习惯是,按照成员定义的顺序进行初始化。

博文 来自: shanshenyuyou

C++初始化列表

阅读数 91

没有更多推荐了,返回首页