c语言的值为什么要初始化(C理解初始化操作)
c语言的值为什么要初始化(C理解初始化操作)#include <iostream> using namespace std; class AA { public: AA() //无参构造函数默认构造函数 { cout<<"我是构造函数,自动被调用了"<<endl; } AA(int _a) //有参构造函数 { a = _a; } AA(const AA &obj2) { cout<<"我也是构造函数,我是通过另外一个对象obj2,来初始化我自己"<<endl; a = obj2.a 10; } ~AA() { cout<<"我是析构函数,自动被调
对于普通变量来说,其初始化和赋值的理解较为简单,只是=操作符在不同的上下文中即可用于初始化,也可用于赋值的更新操作:
int a = 5; // 声明、定义变量的同时初始化
int b(6); // 声明、定义变量的同时初始化
a = 6; // 赋值操作,用于变量的更新
b = 7; // 赋值操作,用于变量的更新
const int c = 8; // 因为是只读,此后不能再更新,自然需要在声明、定义的同时初始化
// const在C中是只读变量,C 视上下文可以实现为只读变量或常量
int arr[5]; // 虽然arr有常量性质,但其元素却是可写的变量
int &r = a; // 引用可以理解为一个实现了自动解引用的有常量性质的指针
C 的类除了封装、继承、多态等特性,还可以通过构造函数来自动初始化数据成员,也可以通过拷贝构造函数通过另一个同类的对象来初始化自己。
C 通过重载来实现拷贝构造和重载=操作符,其关键在于理解初始化操作和赋值操作的区别:
初始化:被初始化的对象正在创建。
赋值:被赋值的对象已经存在。
对于C 的类和对象构造,一些特殊成员需要在创建的同时做初始化,其构造函数的函数体只是赋值的更新操作,为此,C 提供了一种初始化列表的构造机制。
1 定义对象变量并初始化
在拷贝构造函数中,可以用另一个同类对象来初始化自己:
#include <iostream>
using namespace std;
class AA
{
public:
AA() //无参构造函数默认构造函数
{
cout<<"我是构造函数,自动被调用了"<<endl;
}
AA(int _a) //有参构造函数
{
a = _a;
}
AA(const AA &obj2)
{
cout<<"我也是构造函数,我是通过另外一个对象obj2,来初始化我自己"<<endl;
a = obj2.a 10;
}
~AA()
{
cout<<"我是析构函数,自动被调用了"<<endl;
}
void getA()
{
printf("a:%d \n" a);
}
protected:
private:
int a;
};
void ObjPlay01()
{
AA a1; // 变量定义
//用对象1 初始化对象a2、a3,所谓初始化,发生在对象构造的相同时间
AA a2 = a1; //定义对象变量并初始化
AA a3(a1);
a2 = a1; // 用a1来=号给a2 编译器给我们提供的浅copy,发生在对象构造以后
//
}
int main()
{
ObjPlay01();
while(1);
return 0;
}
2 类的对象做函数参数时,实参与形参的结合以及函数返回会调用拷贝构造函数
#include <iostream>
using namespace std;
class Location
{
public:
Location( int xx = 0 int yy = 0 )
{
X = xx ; Y = yy ; cout << "Constructor Object.\n" ;
}
Location( const Location & p ) //拷贝构造函数
{
X = p.X ; Y = p.Y ; cout << "Copy_constructor called." << endl ;
}
~Location()
{
cout << X << " " << Y << " Object destroyed." << endl ;
}
int GetX () { return X ; } int GetY () { return Y ; }
private : int X Y ;
} ;
void f ( Location p )
{
cout << "Funtion:" << p.GetX() << " " << p.GetY() << endl ;
}
void mainobjplay()
{
Location A ( 1 2 ) ; //形参是一个元素,函数调用,会执行实参变量初始化形参变量
f ( A ) ; //实参与形参的结合会调用拷贝构造函数
}
Location g()
{
Location A(1 2);
return A; //函数返回一个对象会调用拷贝构造函数
}
void main()
{
mainobjplay();
g();
system("pause");
}
3 重载赋值运算符
重载的赋值运算符可以用同类的另一个对象来更新自己。
#include <iostream>
using namespace std;
class Name
{
public:
Name(const char *pname)
{
size = strlen(pname);
pName = (char *)malloc(size 1);
strcpy(pName pname);
}
Name(Name &obj) //用obj来初始化自己
{
pName = (char *)malloc(obj.size 1);
strcpy(pName obj.pName);
size = obj.size;
}
~Name()
{
cout<<"开始析构"<<endl;
if (pName!=NULL)
{
free(pName);
pName = NULL;
size = 0;
}
}
void operator=(Name &obj3) // 重载赋值运算符
{
if (pName != NULL)
{
free(pName);
pName = NULL;
size = 0;
}
cout<<"测试有没有调用我……"<<endl;
//用obj3来=自己
pName = (char *)malloc(obj3.size 1);
strcpy(pName obj3.pName);
size = obj3.size;
}
protected:
private:
char *pName;
int size;
};
void playObj()
{
Name obj1("obj1……");
Name obj2 = obj1; //obj2创建并初始化
Name obj3("obj3……");
//重载=号操作符
obj2 = obj3; //=号操作
cout<<"业务操作……5000"<<endl;
}
void main()
{
playObj();
system("pause");
}
4 对象初始化列表
类对象的构造顺序是这样的:
I 分配内存,调用构造函数时,隐式/显示的初始化各数据成员;
II 进入构造函数后在构造函数中执行一般赋值与计算。
在构造对象这种数据类型时,一些数据需要在构造的同时进行初始化,C 编译器使用初始化列表这种机制:
Constructor::Contructor() : m1(v1) m2(v1 v2) m3(v3)
{
// some other assignment operation
}
情况一、数据成员是对象的情况;
如果在类的数据成员中有一个其它类的对象成员,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
情况二、在继承情况下,通过显式调用父类的带参构造函数对父类数据成员进行初始化;
情况三、需要初始化const修饰的类成员
当类成员中含有一个const对象时,也必须要通过成员初始化列表进行初始化,因为这种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。
情况四、需要初始化引用成员数据;
当类成员中含有一个引用时,因为引用是一个有常量性质的特殊指针。
以上种情况以外的其它数据成员,也可以在初始化列表中进行初始化,以获得更高的效率。
成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关。
对于构造函数,按以下顺序调用:基类构造函数,派生类对象成员构造函数,派生类本身的构造函数。
初始化列表先于构造函数的函数体执行。
#include <iostream>
using namespace std;
#include <iostream>
using namespace std;
class Base
{
int n;
public:
Base(int m):n(m){ cout<<"Base is called\n";}
~Base(){}
};
class Other
{
int m;
public:
Other(int a):m(a){ cout<<"Other is called\n";}
};
class Derive:public Base
{
int v;
const int cst;
int &r;
Other obj;
public:
Derive(int a int b int c):Base(a) obj(b) cst(c) r(a)
{
v = a;
cout<<"Self is called\n";
}
~Derive(){}
};
int main()
{
Derive dd(3 4 5);
while(1);
return 0;
}
/*
Base is called
Other is called
Self is called
*/
-End-