C++中的对象初始化
当对象在创建时获得了一个特定的值,我们说这个对象被初始化。初始化不是赋值,初始化的含义是创建变量赋予其一个初始值,而赋值的含义是把当前值擦除,而以一个新值来替代。对象初始化可以分为默认初始化、直接初始化、拷贝初始化以及值初始化。
// (1)默认初始化
int i1;//默认初始化,在函数体之外(初始化为0) int f(void)
{
int i2;//不被初始化,如果使用此对象则报错
} string empty;//empty非显示的初始化为一个空串,调用的是默认构造函数 // (2)拷贝初始化
string str1(,'');//直接初始化
string str2(str1);//直接初始化
string str3 = str1;//拷贝初始化 // (3)值初始化
vector<int> v1();//10个元素,每个元素的初始化为0
vector<string> v2();//10个元素,每个元素都为空 int *pi = new int;//pi指向一个动态分配的,未初始化的无名对象
string *ps = new string;//初始化为空string
int *pi = new int;//pi指向一个未初始化的int int *pi = new int();//pi指向的对象的值为1024
string *ps = new string(,'');//*ps为"9999999999" string *ps1 = new string;//默认初始化为空string
string *ps2 = new string();//值初始化为空string
int *pi1 = new int;//默认初始化
int *pi2 = new int();//值初始化为0
1、C++ Copy初始化
在《inside the c++ object model》一书中谈到copy constructor的构造操作,有三种情况下,会以一个object的内容作为另一个object的初值:
- 第一种情况: XX aa = a;
- 第二种情况: XX aa(a);
- 第三种情况: extern fun(XX aa); fun(a)函数调用
- 第四种情况: XX fun(){...}; XX a = fun();函数返回值的时候
下面我们就上述的四种情况来一一验证:
class ClassTest{
public:
ClassTest()//定义默认构造函数
{
c[] = '\0';
cout << "ClassTest()" << endl;
}
ClassTest& operator=(const ClassTest &ct) //重载赋值操作符
{
strcpy_s(c, ct.c);
cout << "ClassTest& operator=(const ClassTest &ct)" << endl;
return *this;
}
ClassTest(const char *pc)
{
strcpy_s(c, pc);
cout << "ClassTest (const char *pc)" << endl;
}
ClassTest(const ClassTest& ct)//复制构造函数
{
strcpy_s(c, ct.c);
cout << "ClassTest(const ClassTest& ct)" << endl;
}
private:
char c[];
}; ClassTest func(ClassTest temp){
return temp;
} int main(){
cout << "ct1: ";
ClassTest ct1("ab");//直接初始化
cout << "ct2: ";
ClassTest ct2 = "ab";//复制初始化
/*输出说明:
ClassTest ct2 = "ab";
它本来是要这样来构造对象的:首先调用构造函数ClassTest(const char *pc)函数创建一个临时对象,
然后调用复制构造函数,把这个临时对象作为参数,构造对象ct2。然而编译也发现,复制构造函数是
公有的,即你明确地告诉了编译器,你允许对象之间的复制,而且此时它发现可以通过直接调用重载的
构造函数ClassTest(const char *pc)来直接初始化对象,而达到相同的效果,所以就把这条语句优化为
ClassTest ct2("ab")。
*/
cout << "ct3: ";
ClassTest ct3 = ct1;//复制初始化
cout << "ct4: ";
ClassTest ct4(ct1);//直接初始化
cout << "ct5: ";
ClassTest ct5 = ClassTest();//复制初始化
cout << "ct6: ";
ClassTest ct6;//复制初始化
ct6 = "caoyan is a good boy!";
cout << "ct7: ";
ClassTest ct7;
ct7 = func(ct6);
return ;
}
测试结果:
我们可以看到,比较复杂的是ct6和ct7,其中ct6还是比较好理解的,ct7这种情况比较难懂,为什么会有两个拷贝构造函数的调用????
第一次拷贝构造函数的调用:第一次很简单,是因为函数参数的传递,将ct6作为参数传递给temp,用ct6的值初始化temp会调用拷贝构造函数;
第二次拷贝构造函数的调用:因为要返回一个ClassTest对象,我们的编译器怎么做????首先它将temp对象拷贝到func函数的上一级栈帧中,它的上一级栈帧是main函数的栈帧,那么当函数返回时,参数出栈,temp对象的内存空间就会被收回,但是它的值已经被拷贝到main栈帧的一个预留空间中,所以从temp到预留空间的拷贝也是调用拷贝构造函数,最后一步就是给ct7赋值,毫无疑问调用赋值构造函数;对栈帧不同的同学可以看看《程序员的自我修养》一书,里面讲得很详细!
2、初始化列表、构造函数与=赋值之间的区别
总所周知,C++对象在创建之时,会由构造函数进行一系列的初始化工作。以没有继承关系的单个类来看,除了构造函数本身的产生与指定,还涉及到初始化步骤,以及成员初始化方式等一些细节,本篇笔记主要对这些细节进行介绍,弄清C++对象在初始化过程中一些基本运行规则。
class MyCppClass {}
#include <iostream> using std::cout;
using std::endl; class MyCppClass
{
public:
MyCppClass()
{
std::cout <<"In Default Constructor!" <<std::endl;
} MyCppClass(const MyCppClass& rhs)
{
std::cout <<"In Copy Constructor!" <<std::endl;
} MyCppClass& operator= (const MyCppClass& rhs)
{
std::cout <<"In Copy Assignment Operator!" <<std::endl; return *this;
}
}; int main()
{
MyCppClass testClass1; // default constructor
MyCppClass testClass2(testClass1); // copy constructor
testClass1 = testClass2; // copy assignment operator MyCppClass testClass3 = testClass1; // copy constructor return ;
}
执行结果:
// 数据成员类型为内置类型
class MyCppClass
{
public:
// 赋值操作进行成员初始化
MyCppClass
{
counter = ;
} // 初始化列表进行成员初始化
MyCppClass : counter()
{
} private:
int counter;
}
当类的数据成员类型为内置类型时,上面两种初始化方式的效果一样。当数据成员的类型同样也为一个类时,初始化的过程就会有不一样的地方了,比如:
// 数据成员类型为自定义类型:一个类
class MyCppClass
{
public:
// 赋值操作进行成员初始化
MyCppClass(string name)
{
counter = ;
theName = name;
} // 初始化列表进行成员初始化
MyCppClass : counter(), theName(name)
{
} private:
int counter;
string theName;
}
在构造函数体内的theName = name这条语句,theName先会调用string的default constructor进行初始化,之后再调用copy assignment opertor进行拷贝赋值。而对于初始化列表来说,直接通过copy constructor进行初始化。
明显起见,可以通过如下的代码进行测试。
#include <iostream>
#include <string> class SubClass
{
public:
SubClass()
{
std::cout <<" In SubClass Default Constructor!" <<std::endl;
} SubClass(const SubClass& rhs)
{
std::cout <<" In SubClass Copy Constructor!" <<std::endl;
} SubClass& operator= (const SubClass& rhs)
{
std::cout <<" In SubClass Copy Assignment Operator!" <<std::endl; return *this;
}
}; class BaseClass
{
public:
BaseClass(const SubClass &rhs)
{
counter = ;
theBrother = rhs;
std::cout <<" In BaseClass Default Constructor!" <<std::endl;
} BaseClass(const SubClass &rhs, int cnt):theBrother(rhs),counter(cnt)
{
std::cout <<" In BaseClass Default Constructor!" <<std::endl;
} BaseClass(const BaseClass& rhs)
{
std::cout <<" In BaseClass Copy Constructor!" <<std::endl;
} BaseClass& operator= (const BaseClass& rhs)
{
std::cout <<" In BaseClass Copy Assignment Operator!" <<std::endl; return *this;
}
private:
int counter;
SubClass theBrother;
}; int main()
{
SubClass subClass; std::cout <<"\nNo Member Initialization List: " <<std::endl;
BaseClass BaseClass1(SubClass); std::cout <<"\nMember Initialization List: " <<std::endl;
BaseClass BaseClass2(SubClass, ); return ;
}
执行结果:
为了更好的理解它们,先对C++当中的数据类型进行简单划分。在C++里面,数据类型大致可以分为两种:第一种是内置类型,比如float,
int, double等;第二种是自定义类型,也就是我们常用的class,
struct定义的类。在对这些类型的数据进行初始化时,差别就体现出来了:对于内置类型,在使用之前必须进行显示的初始化,而对于自定义类型,初始化责任则落在了构造函数身上。
int x = 0; // 显示初始化x
SubClass subClass; // 依赖SubClass的default constructor进行初始化
上面的名词“缺省初始化”描述的就是当内置类型或者自定义类型的数据没有进行显示初始化时的一种初始化状态。而“隐式初始化”描述的是在该状态下面进行的具体操作方式,比如对于内置类型来说,缺省初始化状态下进行的隐式初始化实际上是未定义的,而自定义类型的隐式初始化则依赖于其constructor。
- 对象被用来初始化一个容器元素
- 为映射表添加一个新元素,对象是这个添加动作的副作用
- 定义一个特定长度的容器,对象为容器的元素
#include <iostream>
#include <vector>
#include <map>
#include <string> using std::cout;
using std::endl;
using std::vector;
using std::map;
using std::string; class NumbericInitTestClass
{
public:
void PrintCounter()
{
cout <<"counter = " <<counter <<endl;
}
private:
int counter;
}; int main()
{
NumbericInitTestClass tnc;
tnc.PrintCounter(); map<string, int> mapTest;
cout <<mapTest["me"] <<endl; vector<NumbericInitTestClass> vecNumbericTestClass();
vecNumbericTestClass[].PrintCounter(); return ;
}
参考文章
C++中的对象初始化的更多相关文章
- Hive_UDF函数中集合对象初始化的注意事项
UDF函数中定义的集合对象何时初始化 udf函数放在sql中对某个字段进行处理,那么在底层会创建一个该类的对象,这个对象不断的去调用这个evaluate(...)方法,截图如下: 1.1 如果说对 ...
- Java中子类对象初始化的过程
Java中的继承机制看似简单,实际上包含了很多细节.最近在刷题过程中屡屡跳坑,于是自己仔细再学习了一下Java中子类初始化的细节,与大家分享. class Father { Father(){}; } ...
- AJPFX关于面向对象中的对象初始化整理,综合子父类、代码块等等
今天总结了一下子父类当中含有静态代码块.代码块.构造函数.成员变量.子类复写父类方法时子类的初始化过程,把思路理清一下 class Fu { //父类成员变量 private int num = 3; ...
- javascript 中的对象初始化
参考 developer.mozilla.org 网站.这个是一个前端必须经常光顾的网站. 记录一下对象的创建方法,虽然很简单,但是确需要非常注意. Objects can be initialize ...
- C++ 匿名对象初始化新对象
//c++中匿名对象初始化新对象 #include<iostream> using namespace std; class Point{ public: Point(){ cout &l ...
- Java对象初始化
自动初始化(默认值) 一个类的所有基本数据成员都会得到初始化,运行下面的例子可以查看这些默认值: class Default{ boolean t; char c; byte b; short s; ...
- C++中对象初始化
在C++中对象要在使用前初始化,永远在使用对象之前先将它初始化. 1.对于无任何成员的内置类型,必须手工完成此事. 例如: int x=0; double d; std::cin>>d; ...
- SKSpriteNode对象初始化在iPhone 6 plus中显示不正确的分析及解决
一个SpriteKit项目在其他设备上运行都无问题(无论是真机或是模拟器),但是在iPhone6 Plus上会出现精灵对象纹理被过度放大的现象: 从上图中大家可以看到无论是主角或是道具球都过大了. 看 ...
- C#中的自动属性、隐式类型var、对象初始化器与集合初始化器、扩展方法
1.自动属性(Auto-Implemented Properties) //以前的写法 .net2.0 private string _userName; public string UserName ...
随机推荐
- Linux中rz和sz命令用法详解
http://www.111cn.net/sys/linux/67810.htm 在linux中rz 和 sz 命令允许开发板与主机通过串口进行传递文件了,下面我们就来简单的介绍一下rz 和 sz 命 ...
- 匿名函数gc分析
测试一:使用member function创建action会产生gc,不管该函数是否访问外部变量: private System.Action memberAct = null; // gc 112B ...
- Sword 内核队列一
1.gfifo概述 gfifo是一个First In First Out数据结构,它采用环形循环队列的数据结构来实现:它提供一个无边界的字节流服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只 ...
- RTT(往返时间)和RPC
RTT(Round-Trip Time)往返时间在计算机网络中它是一个重要的性能指标.表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认,不包含数据传输时间)总共经 ...
- 12 Best Live Chat Software for Small Business Compared (2019) 最佳的wordpress在线聊天工具推荐插件 来帮你和潜在客户互动
12 Best Live Chat Software for Small Business Compared (2019) Did you know that more than 67% of ...
- CDN 服务域名解析配置
记录类型请选择为CNAME: 主机记录即加速域名的前缀,例如: 如果您的加速域名为... 前缀为... testcdn.aliyun.com testcdn www.aliyun.com www ...
- sbt spark2.3.1 idea打包Caused by: java.lang.ClassNotFoundException: scala.Product$class
今天同事在服务区上面装的是最新版本的hadoop3.10和spark2.3.1,因为用scala开发, 所以我想用sbt进行开发.过程中遇到各种坑,刚开始用的jdk10,结果也报错,后来改成jdk1. ...
- NuGet Packages are missing,This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.
错误内容 This project references NuGet package(s) that are missing on this computer. Use NuGet Package R ...
- windows pm2 启动nodejs失败:Error: EBADF: bad file descriptor, uv_pipe_open
windows下打开命令窗口,安装pm2:npm install pm2 -g pm2成功安装,在项目目录下用pm2启动服务:pm2 start index.js,结果启动失败,错误如下: .pm2\ ...
- MFMailComposeViewController发送邮件的实例
本文转载至 http://blog.csdn.net/liufeng520/article/details/7585140 iPhone API已经提供了系统写邮件界面的接口,使用MFMailCo ...