当前位置:首页 > 芯闻号 > 充电吧
[导读]一.声明(Declaration)       声明的作用是指定变量的类型和名称,makes a name known to the program。区分声明和定义可以让C++支持分开编译,比如A.c

一.声明(Declaration)
       声明的作用是指定变量的类型和名称,makes a name known to the program。区分声明和定义可以让C++支持分开编译,比如A.cpp中定义了变量var1,在B.cpp中只需要声明var1这个变量就可以直接使用。因为这样的用法,声明常常见于头文件中。源文件包含头文件之后,就可以使用这个变量,即使没有看到该变量的定义。 声明的语法如下:

extern int i; // object declaration
int numDigits(int number); // function declaration
class Widget; // class declaration

template// template declaration
class GraphNode;

extern double pi = 3.1416; // definition

二.定义(Definition)
       定义是为变量分配存储空间,并可能进行初始化。定义是一种声明,因为定义的同时必然会指定变量的类型和名称,然而声明却不是定义。C++中变量的定义必须有且仅有一次,而变量的声明可以多次。变量一般不能定义在头文件中,除了const变量(local to a file)。
       除了变量,类和函数也有定义的说法,总结如下:
1.对于类来说,一般定义在头文件中。因为编译器需要在每个源文件都看到类的定义才能编译成功;
2.对于一般函数来说,函数声明在头文件中,函数定义则在源文件中;
3.对于inline和constexpr function,编译器需要在每个源文件都看到定义,因此通常也定义在头文件中。

int x; // declaration or definition

       上面单独的一行,是声明还是定义,判断的原则是看是否占用内存(能否进行初始化)。例如:

class MyClass // 类定义
{
   int x; // 它是声明,以为C++11之前是不允许在类的定义内部直接初始化数据成员的 
   float y = 10.0f; // C++11及以后支持这种写法,但它仍然是个声明
   static char c;   // 这也是个声明,因为如果写成这样 static char c = 'A';
                    // 编译器会报错,你需要在类外进行定义并初始化,因为类里面的只是声明而已
};

       但是如果int x;出现在函数定义内部,它就是一个定义了。例如:

int nurnDigits(int number) // function definition
{
    int x; // object definition,因为此时x是可以被初始化或赋值的
    x = number/10;
    return x;
}
class Widget // class definition
{ 
public:
    Widget(); // function declaration
    ~Widget();
private:
    int x; // object declaration
    int y;
}
template// template definition
class GraphNode 
{
public:
    GraphNode();
    ~GraphNode();
    ......
}

       这里有一个令人疑惑的地方,头文件的的类MyClass既然是定义,按照“定义”的解释,它应该占有内存,那为何类中包含的内容反而是声明。
       因为类是属于用户自定义的数据类型,与内置类型,比如说int,在使用上类似。类定义只是定义了一种类型,也即说明了一个类,并没有实际定义类的对象,定义的是类,定义类描述的是新的类型,而描述新类型并不会开辟内存空间去存储这样一种新类的对象。
三.初始化(Initialization)

       初始化是指变量在创建的同时获得的初始值。虽然C++经常用=来初始化一个变量,但是赋值和初始化是两种不同的操作。赋值是变量定义后的操作,效果是改变变量的值,或者说是用新值来替换旧值;而初始化是在变量创建期获得一个值。两者具有本质的区别。下面分别介绍一下C++常见的初始化方式:

default initialization

       当我们定义一个变量时,不提供initializer,那么这个变量就是默认初始化(default initialized)的。默认值由变量的类型和变量的定义位置来决定。
对于built-in type,默认值由变量的定义位置决定。在函数外部定义的全局变量(global variable),函数内部定义的局部静态变量(local static object)全部初始化为0。函数内部定义的局部变量是未初始化的;使用未初始化的变量值的行为是未定义的,会带来巨大的潜在风险。

       对于class type,由类里的默认构造函数初始化。如果类定义里没有默认构造函数(显示或隐示),则编译出错。

#includeusing namespace std;

int a;

int main()
{
   static int b;
   int c;

   cout << a << endl;
   cout << b << endl;
   cout << c<< endl;
   system("pause");
   return 0;
}

       在VS执行这段代码,输出变量a的值0,b的值为0,同时VS会报错:Run-Time Check Failure #3 — The variable 'c' is being used without being initialized。 变量a和b被默认初始化为0,变量c未被初始化。

list initialization

       C++11中提供了一种新的初始化方式,list initialization,以大括号包围。A tour of c++中写到The = form is traditional and dates back to C, but if in doubt, use the general {}-list form。注意这种初始化方式要求提供的初始值与要初始化的变量类型严格统一,用法如下,

// built-in type initialization
double d1{2.3};    //ok: direct-list-initialization 
double d2 = {2.3}; //ok: copy-list-initialization
// class type initialization
complexz2{d1,d2};
complexz3 = {1,2};  //ok: the = is optional with {...}
vectorvec{1,2,3,4,5,6};//ok: a vector of ints

long double pi = 3.1415;
int a{pi}, b = {pi};         //error: narrowing conversion required
int c(pi), d = pi;	     //ok: implict conversion.

value initialization
       value initialization里,built-in type变量被初始化为0,class type的对象被默认构造(一定要有)初始化。这种方式通常见于STL里的vector和数组,且经常与list initialization结合起来使用,为我们初始化全0数组提供了很大的便利。简单用法如下:

vectorivec(10);			//ten elements, each initialized to 0
vectorsvec(10);		//ten elmenets, each an empty string
vectorv1 = {"a", "an", "the"}; //list initialized
int a[10] = {};				//ten elements, each initialized to 0
int a2[] = {1,2,3};			//list initialized
int a3[5] = {1,2,3};			//equivalent to a3[] = {1,2,3,0,0}

关于类的初始化比较复杂,整理几点:
1.编译器首先编译类成员的声明,包括函数和变量
2.整个类可见后,才编译函数体(所以不管定义顺序,函数里可以用类里的任何变量和函数)
3.C++11提供了in-class initializers机制,C++ Primer里面讲如果编译器支持,推荐使用in-class initializers机制。注意这种机制只支持=,{}形式,不支持()。Constructor Initializer List对变量进行初始化后,才进入构造函数。Constructor Initializer List里忽略的成员变量(为空则相当于全部忽略),会由in-class initializers初始化,或者采取default initialization,然后进入构造函数体,构造函数体实际是给成员二次赋值
4.对于class type成员,会调用其默认构造函数进行default initialization。

5.对于built-in type成员,要么in-class initialization,要么Constructor initializer list。是否会被default initialization与类定义的位置有关,这点和“default initialization”小节中说的built-int type类似

6.类的静态函数成员可以在类内部或者外部定义,而静态数据成员(const除外)则只能在外部定义以及初始化

#includeusing namespace std;

class testA
{
public:
	testA()
	{
		cout << "A-x:" << x << endl;
		cout << "A-y:" << y << endl;
	}
private:
	int x;
	int y = 10; // in-class initializer
};

class testB
{
public:
	void printf() const
	{
		cout << "B:" << data << endl;
	}
private:
	int data;
	testA a;
};

testB b1;

int main()
{
	b1.printf();
	testB b2;
	b2.printf();

	system("pause");
	return 0;
}

如果是动态初始化的对象,输出结果和上图一样,代码如下:

#includeusing namespace std;

class testA
{
public:
	testA()
	{
		cout << "A-x:" << x << endl;
		cout << "A-y:" << y << endl;
	}
private:
	int x;
	int y = 10; // in-class initializer
};

class testB
{
public:
	void printf() const
	{
		cout << "B:" << data << endl;
	}
private:
	int data;
	testA a;
};

testB *b1=new testB();

int main()
{
	b1->printf();
	testB *b2 = new testB;
	b2->printf();

	system("pause");
	return 0;
}

       但是如果在main函数中,对b2进行value initialization,即将testB *b2 =new testB;改成testB *b2 =new testB();,那么类中的built-in type成员都会被default initialization了。输出结果如下所示:


       还需注意的是数组的初始化。

       定义数组时,如果没有显示提供初始化列表,则数组元素的默认化初始规则同普通变量一样:函数体外定义的内置类型数组,其元素初始为0;函数体内定义的内置类型数组,其元素无初始化;类类型数组无论在哪里定义,皆调用默认构造函数进行初始化,无默认构造函数则必须提供显示初始化列表。
       如果定义数组时,仅提供了部分元素的初始列表,其剩下的数组元素,若是类类型则调用默认构造函数进行初始,若是内置类型则初始为0(不论数组定义位置)。
       对于动态分配的数组,如果数组元素是内置类型,其元素无初始化;如果数组元素是类类型,依然调用默认构造函数进行初始化。也可以在使用跟在数组长度后面的一对空圆括号对数组元素做值初始化。
例如: int *ptrA = new int[10];
    int *ptrB = new int[10] ();
       其中ptrA指向的动态数组其元素未初始化,而ptrB指向的动态数组元素被初始化为0。

四.赋值(Assignment) 
       赋值的结果是左边的操作元,为左值,也就是说,下面的写法语法正确

int a = 0;
(a = 0) = 1; // the final value of a is 1

       因为赋值操作符的优先级很低,该带括号的时候不能遗漏。
       顺便提一下++i和i++的区别:前者将操作元增加,并且返回改变后的操作元;后者将操作数增加,返回原先值得拷贝作为结果。前置自增返回的结果是左值,后置自增返回的是右值。前置自增操作符做的无用功少,虽然C++编译器对int和指针类型的后置自增操作符作了优化,C++ Primer推荐如无特殊需求,优先使用前置自增操作符。

       数组不支持拷贝初始化或者将一个整体赋值给另一个数组。

int a[] = {0,1,2} int a2[] = a; // error: cannot assign one array to another



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭