早在学习语言之初,我们就学习过一系列成员初始化方式:初始化列表、拷贝初始化、赋值语句。下面我们按照先后顺序讲解在C++中,这三种初始化方式的实现。
对象初始化之列表初始化例如:
int arr[]={1,2,3,4,5};
我们学习了类、结构体的知识以后,也学习了对应的构造函数的知识。然而我们的构造函数,通常一次只能给一个对象进行初始化,当然,我们也可以利用数组,一个构造多个对象。但这样操作并不是最优的方案。假如我们自己构造了一个数组类型,那么我们能不能进行初始化列表的操作呢?答案是可以的。
注意,调用该方法需要引入标准库:iostream,否则无法使用
initializer_list对象名
用{}限定的类型T元素的列表是以标准库initializer_list 对象的形式呈现给我们的。
initializer_list 对象的成员方法:
- size()
- begin()
- end()
一般,我们通过size获取初始化列表中元素的个数,通过begin~end再结合copy函数来进行初始化列表的值拷贝。
size()用来明确我们需要多长的数组,begin()和end()用来将值拷贝到你的数组中。
copy函数:copy(begin(),end(),aim_arr)示例:
Vector_(initializer_listlst) :sz{ int(lst.size()) }, elem{ new double[sz] } //长度初始化 { copy(lst.begin(), lst.end(), elem); //内容初始化 }//核心是调用initializer_list T
此处使用的初始化方法要重点把握
对象初始化之拷贝构造函数例如:
int a = 10; int b = a; b=100; cout<输出结果:
10 100在上面的示例中,b获取的并不是a的地址,而是a的一个副本。实际上,这种效果并不是系统默认的情况,系统默认的情况实际上b会直接获取的为a的地址。
如果我们自己构造一个新的数据类型例如:
先给出头文件:
#pragma once #includeusing namespace std; class Vector_ { int sz; double* elem;//可用于申请存储块,这样就不会出现元素个数固定的问题 public: Vector_(int size) :sz{ size }, elem{ new double[size] } { for (int i = 0; i < size; i++) { elem[i] = 0; }//初始化默认值 };//构造函数,用以申请存储空间 ~Vector_() { delete[] elem;//析构函数释放空间 } Vector_(initializer_list lst) :sz{ int(lst.size()) }, elem{ new double[sz] } //长度初始化 { copy(lst.begin(), lst.end(), elem); //内容初始化 }//核心是调用initializer_list T int size() const{ return this->sz; }//用以返回vector长度 double get(int n)const { return elem[n]; } //读 void set(int n, double v) { elem[n] = v; } //写 }; 使用第一种构造函数:
Vector_ V(5); cout << "自定义类型变量原值:" << endl; for (int i = 0; i < V.size(); i++) { V.set(i, 1.1 * i); cout << "v[" << i << "]==" << V.get(i) << endl; } cout << endl << endl; Vector_ V_ = V; V_.set(0, 1.111); cout << "用赋值方式给V_初始化,并修改V_的首元素,然后再打印原值:" << endl; for (int i = 0; i < V.size(); i++) { cout << "v[" << i << "]==" << V.get(i) << endl; } cout << endl << endl;输出结果:
5 自定义类型变量原值: v[0]==0 v[1]==1.1 v[2]==2.2 v[3]==3.3 v[4]==4.4 用赋值方式给V_初始化,并修改V_的首元素,然后再打印原值: v[0]==1.111 v[1]==1.1 v[2]==2.2 v[3]==3.3 v[4]==4.4由输出结果我们可以看到:V_得到的是V的地址,而并非其副本。若修改V _等价于修改V。这显然不是我们想要的结果。因为我们的拷贝任务我想要让V _获取V的副本,这样我们可以直接操作V _而不影响V。
我们如何解决这个问题呢?下面我们介绍一个新的巧妙方法:
拷贝构造函数://声明 Vector_ (const Vector_& arg);//实现 Vector_(const Vector_& V) :sz{ V.sz }, elem{ new double[V.sz] } { copy(V.elem, &V.elem[V.sz], elem); }输入:
Vector_ V(5); cout << "自定义类型变量原值:" << endl; for (int i = 0; i < V.size(); i++) { V.set(i, 1.1 * i); cout << "v[" << i << "]==" << V.get(i) << endl; } cout << endl << endl; Vector_ V_ = V; V_.set(0, 1.111); cout << "用赋值方式给V_初始化,并修改V_的首元素,然后再打印原值:" << endl; for (int i = 0; i < V.size(); i++) { cout << "v[" << i << "]==" << V.get(i) << endl; } cout << endl << endl; cout << "用赋值方式给V_初始化,并修改V_的首元素,然后再打印V_:" << endl; for (int i = 0; i < V_.size(); i++) { cout << "v_[" << i << "]==" << V_.get(i) << endl; } cout << endl << endl;输出结果:
自定义类型变量原值: v[0]==0 v[1]==1.1 v[2]==2.2 v[3]==3.3 v[4]==4.4 用赋值方式给V_初始化,并修改V_的首元素,然后再打印原值: v[0]==0 v[1]==1.1 v[2]==2.2 v[3]==3.3 v[4]==4.4 用赋值方式给V_初始化,并修改V_的首元素,然后再打印V_: v_[0]==1.111 v_[1]==1.1 v_[2]==2.2 v_[3]==3.3 v_[4]==4.4通过上面的结果,我们可以得到结论:利用拷贝构造函数可以通过赋值的方式获取被复制变量的副本而非地址。
拷贝赋值我们实现了拷贝构造函数以后,仍存在一个问题:
int a = 1; int b = 2; b = a; cout<<"b初次变值:"<输出结果:
b初次变值:1 b二次变值:2 此时a值:1普通类型数据进行值交换并无任何异样,但是如果我们采用的是自定义类型的数据变量,效果就会不一样:
Vector_ V(5); cout << "自定义类型变量原值:" << endl; for (int i = 0; i < V.size(); i++) { V.set(i, 1.1 * i); cout << "v[" << i << "]==" << V.get(i) << endl; } cout << endl << endl; Vector_ V_ = V; V_.set(0, 1.111); V_.set(1, 6.666); cout << "用赋值方式给V_初始化,并修改V_的首元素,然后再打印V_:" << endl; for (int i = 0; i < V_.size(); i++) { cout << "v_[" << i << "]==" << V_.get(i) << endl; } cout << endl << endl; V_ = V; cout << "用赋值方式给V_再赋值,用V覆盖V_原值:" << endl; for (int i = 0; i < V_.size(); i++) { cout << "v_[" << i << "]==" << V_.get(i) << endl; } cout << endl << endl;输出结果:
自定义类型变量原值: v[0]==0 v[1]==1.1 v[2]==2.2 v[3]==3.3 v[4]==4.4 用赋值方式给V_初始化,并修改V_的首元素,然后再打印V_: v_[0]==1.111 v_[1]==6.666 v_[2]==2.2 v_[3]==3.3 v_[4]==4.4 用赋值方式给V_再赋值,用V覆盖V_原值: v_[0]==0 v_[1]==1.1 v_[2]==2.2 v_[3]==3.3 v_[4]==4.4由结果可见,我们成功完成了值覆盖。
我们完成值交换后,再尝试修改V_,我们看看会发生什么情况?V_.set(0, 8888.88); //修改V_ cout << "修改V_后,V的变化情况:" << endl; for (int i = 0; i < V.size(); i++) { cout << "v[" << i << "]==" << V.get(i) << endl; } cout << endl << endl;输出结果:
修改V_后,V的变化情况: v[0]==8888.88 v[1]==1.1 v[2]==2.2 v[3]==3.3 v[4]==4.4我们发现,我们自定义的数据类型进行常规的值覆盖语句的时候,得到的并不是值本身的覆盖,而是指针的覆盖。这样就会导致像我们没有提出拷贝构造函数之前一样的问题。那么我们该如何解决这个问题呢?下面我们就需要用到运算符重载:
学习过运算符重置基础知识以后,这个问题就变得非常的简单。如果你还没有学习运算符的重载知识,不建议继续往下阅读,而是回过头好好学习一下运算符的重载。
下面我们开始解决问题:
拷贝赋值函数:
//拷贝赋值 Vector_& Vector_::operator=(Vector_& v) { double* p = new double[v.sz]; copy(v.elem, &v.elem[v.sz], p); delete[] elem; elem = p; sz = v.sz; return *this; } //考虑到空间的开销,这里不用内联,仅在类体中保留一份声明代码的思路是,仿照被复制的那个变量,再开辟与其一模一样的空间然后进行复制。也许进行赋值运算的左值原本就并非为空,所以代码还要考虑将原先开辟的空间释放掉(这段代码中this指针的使用方式可以模仿)。
验证:
创建被拷贝变量V1:
Vector_ V1 = { 1.2,1.3,1.4,1.5,1.6 };创建V2拷贝V1并输出V2:
Vector_ V2; V2 = V1; cout<<"第一次输出:"<cout << "V2[" << i << "]==" << V2.get(i) << endl; } 输出结果:
第一次输出: V2[0]==1.2 V2[1]==1.3 V2[2]==1.4 V2[3]==1.5 V2[4]==1.6修改V2:
V2.set(0,99.9999);输出V1观察是否被修改:
cout << endl << endl << endl << "输出V1观察是否变化:" << endl; for (int i = 0; i < V1.size(); i++) { cout << "V1[" << i << "]==" << V1.get(i) << endl; }输出结果:
第一次输出: V2[0]==1.2 V2[1]==1.3 V2[2]==1.4 V2[3]==1.5 V2[4]==1.6 输出V1观察是否变化: V1[0]==1.2 V1[1]==1.3 V1[2]==1.4 V1[3]==1.5 V1[4]==1.6结果正确,拷贝赋值函数完成。
(文章作学习笔记使用,本人水平有限,若有纰漏,欢迎指正)



