一文带你分清C++的定义,声明和初始化
定义
变量的定义用于为变量分配存储空间,还可以为变量指定初始值。
int units_sold; double sales_price, avg_price; std::string title; Sales_item curr_book; // class Sales_item
初始化
C++ 支持两种初始化变量的形式:复制初始化和直接初始化。复制初始化语法用等号(=),直接初始化则是把初始化式放在括号中。
int ival(1024); // direct-initialization int ival = 1024; // copy-initialization
初始化不是赋值。初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替。
当定义没有初始化式的变量时,系统有时候会帮我们初始化变量。
1.内置类型变量
(Built-in Types,即int,float,double,void,char,bool等。注意string是标准库定义的类型,不是内置类型)
在函数体外定义的变量都初始化成 0,在函数体里定义的内置类型变量不进行自动初始化。
2.类
类通过定义一个或多个构造函数来控制类对象的初始化。创建类类型的新对象,都要执行构造函数,保证每个对象的数据成员具有合适的初始值。
构造函数可以包含一个构造函数初始化列表,以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。与任意的成员函数一样,构造函数可以定义在类的内部或外部。
构造函数初始化只在构造函数的定义中而不是声明中指定。
//将 isbn 成员初始化为 book 形参的值,将 units_sold 和 revenue 初始化为 0。 Sales_item::Sales_item(const string &book): isbn(book), units_sold(0), revenue(0.0) { }
如果没有提供初始化式,那么就会使用默认构造函数。如果类具有默认构造函数,那么就可以在定义该类的变量时不用显式地初始化变量。例如,string 类定义了默认构造函数来初始化 string 变量为空字符串。
string a; cout << "a: " << a <<endl;
输出:
a:
此外,省略初始化列表在构造函数的函数体内对数据成员赋值是合法的。
Sales_item::Sales_item(const string &book) { isbn = book; units_sold = 0; revenue = 0.0; }
不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。初始化发生在计算阶段开始之前。(也就是函数体执行以前)→ 这里似乎有些难以理解,通过后文的实例也许你能明白
在构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化。运行该类型的默认构造函数,来初始化类类型的数据成员。内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为 0。
未初始化的变量
局部作用域的内置类型变量将不被自动初始化,这可能导致其成为未初始化的变量。这是常见的程序错误,但编译器无法检测出所有未初始化变量的使用。→ 你肯定可以理解这可能导致的灾难性后果了(这竟然不可以运行,为什么呢?这竟然可以运行,为什么呢?.jpg)再稍微解释一下原因:问题出在未初始化的变量事实上都有一个值。编译器把该变量放到内存中的某个位置,而把这个位置的无论哪种位模式都当成是变量初始的状态。当被解释成整型值时,任何位模式都是合法的值——虽然这个值不可能是程序员想要的。因为这个值合法,所以使用它也不可能会导致程序崩溃。可能的结果是导致程序错误执行和/或错误计算。
声明
声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。
可以通过使用extern关键字声明变量名而不定义它。extern 声明不是定义,也不分配存储空间。事实上,它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。
extern int i; // declares but does not define i int i; // declares and defines i
只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为 extern:
extern int i = 10; //defines i
在 C++ 语言中,变量必须且仅能定义一次,而且在使用变量之前必须定义或声明变量。
实例
#include <iostream> #include <string> using namespace std; //类x的声明 //如果这一部分放在main()函数后面,报错:error: 'x' was not declared in this scope //在实际工程中,这部分声明将放在头文件(.h)中,而构造函数及成员函数的定义则放在.cpp文件中 class x{ public: x(int a, int b, string c); void print_data(); private: //类数据成员的变量名最好在开头加一个字母m(即member) int ma; int mb; string mc; }; int main(){ int a1(2); //直接初始化 int b1 = 3; //复制初始化 string c1; //默认构造函数初始化string变量为空字符串 c1 = "dwkw"; //赋值 x data(a1, b1, c1); //调用构造函数初始化 return 0; print_data(); } //构造函数定义 x::x(int a, int b, string c):ma(a), mb(b), mc(c){} //成员函数定义 void x::print_data(){ cout << "ma: " << ma << endl; cout << "mb: " << mb << endl; cout << "mc: " << mc << endl; }
输出:
ma: 2
mb: 3
mc: dwkw
声明时提供初值
如果在类的声明中就对数据成员提供初值,而不在初始化列表中提供,程序可以执行,输出ma的值为1。
class x{ public: x(int a, int b, string c); void print_data(); private: int ma = 1; //声明时提供初值 int mb; string mc; }; //去掉初始化列表 x::x(int a, int b, string c):mb(b), mc(c){}
这一做法在早期版本不予支持,但从c++11就可以了。[2]
不过这破坏了类的抽象性,并不建议这样做。
查看c++版本的方法:[3]
cout << __cplusplus << endl; //输出c++版本
在构造函数内赋初值,而不用列表
前面提到省略初始化列表在构造函数的函数体内对数据成员赋值是合法的。
//去掉初始化列表,在构造函数体内赋值 //其它代码保持不变 x::x(int a, int b, string c){ cout << "赋值前: " << endl; print_data(); cout << "赋值后: " << endl; ma = 4; mb = 5; mc = "ser"; }
输出:
赋值前:
ma: 4199744
mb: 0
mc:
赋值后:
ma: 4
mb: 5
mc: ser
实际上我就没写初始化列表,但系统它就会在这里执行初始化。总之就会在执行构造函数体内的语句之前初始化(如果它可以自动初始化),即使根本没写初始化列表。→ 啧,我就像在说绕口令,希望你能明白我的意思
但是ma和mb都是局部作用域(我不确定类作用域是否是局部作用域,但从输出来看,ma不是0,所以应该没有能够初始化)的内置类型变量,不进行自动初始化;mc有默认构造函数,自动初始化为空字符串。
而后,执行构造函数体内部的语句,将对ma和mb进行初始化(我想这里应该是初始化而不是赋值),对mc赋值。
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注猪先飞的更多内容!
原文出处:https://blog.csdn.net/qq_44920947/article/details/123216283
相关文章
- vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
- 这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
- 这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
- 本文主要对C# 类的声明进行详细介绍。具有一定的参考价值,下面跟着小编一起来看下吧...2020-06-25
- 整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
- 本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
- 这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- 自定义一个jquery模态窗口插件,将它集成到现有平台框架中时,它只能在mainFrame窗口中显示,无法在顶层窗口显示. 解决这个问题的办法: 通过以下代码就可能实现在顶层窗口弹窗 复制代码 代码如下: $(window.top.documen...2014-05-31
- 这篇文章主要介绍了自定义feignClient的常见坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-20
- 这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- 本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
- 今天小编就为大家分享一篇pytorch 自定义卷积核进行卷积操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-05-06
- 这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
PHP YII框架开发小技巧之模型(models)中rules自定义验证规则
YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证...2015-11-24