//明确拷贝构造函数是在对象初始化时调用 例如ojbect A(B);
//当已经定义了一个对象,比如 object A; 再对A赋值 A=B,如果自己没用定义=重载函数,编译器将提供浅拷贝
//如果自己定义了重载=,那么将调用自己的重载的函数(这是为了避免浅拷贝)
#include<iostream>
class AA
{
public:
AA()
{
std::cout << "我是构造函数,自动调用了" << std::endl;
}
AA(int a)
{
this->a = a;
}
AA(const AA&obj2)//拷贝构造函数
{
std::cout << "我也是构造函数,我是通过另外一个对象ojb2,来初始化ojb1.来初始我自己" << std::endl;
}
~AA()
{
std::cout << "我是析构函数,自动被调用了" << std::endl;
}
void getA()
{
printf("a:%d\n", a);
}
protected:
private:
int a;
};
void kk()
{
AA a1;//变量定义,将自动调动无惨构造函数
//AA a2 = a1;//自动调动拷贝构造函数(如果自己没有定义拷贝构造函数,系统自己讲提供默认拷贝构造函数
AA a2;
// 如果是这样 AA a2;//先调用无惨构造函数
a2=a1;//如果没有定义=重载函数,编译器自动提供浅拷贝,copy不一定要构造函数
}
void jj()
{
AA a1(10);//自动调用有一个参数的构造函数
AA a2(a1);//自动调用拷贝构造函数
}
void main()
{
kk();
system("pause");
}
//关于对象作为参数,传递参数时,将调用拷贝构造函数
#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;
};
//alt + f8 排版
void f(Location p)//此对象的生命周期就是这个函数范围
{
cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}
void mainobjplay()
{
Location A(1, 2);
f(A);//将调用复制构造函数
}
void main()
{
mainobjplay();
system("pause");
}
关于返回值为对象时调用拷贝构造函数
//如果没有没有定义=运算符重载,那么使用系统默认的
#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;
};
//alt + f8 排版
void f(Location p)
{
cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}
Location g()
{
Location A(1, 2);
return A;//返回时,先调用复制构造函数,将A复制给一个匿名对象,匿名对象在赋值给B
}
//40 =等号操作
//42 对象初始化操作
//对象初始化操作 和 =等号操作 是两个不同的概念
void mainobjplay()
{
/*Location B;//如果这样将自动调用构造函数
B = g();//将匿名对象赋值给B,
//析构A,匿名对象,析构B*/// 3次
Location B = g();
//如果返回的匿名对象,来初始化另外一个同类型的类对象,那么匿名对象会直接转成新的对象。。。
//这样匿名对象就等于B,析构A,再析构B(等于析构匿名对象) 2次
//匿名对象的去和留,关键看,返回时如何接过来。
cout << "测试测试" << endl;
}
void main()
{
mainobjplay();
system("pause");
}
对象的初始化 和 对象之间=号操作是两个不同的概念(急需注意指针指向另外一个内存时,需要释放开始指向的内存)
#define _CRT_SECURE_NO_WARNINGS
#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");
}
关于拷贝构造函数规则研究
//如果你写了copy构造函数,那么编译器不会在提供无参构造函数
//如果你写了有参或者无参构造函数,那么编译器也不会提供无参构造函数
#include "iostream"
using namespace std;
class Test
{
public:
//如果你写了copy构造函数,那么编译器不会在提供无参构造函数
Test(Test &obj)
{
cout<<"我是copy构造函数"<<endl;
//a = 1;
//b = 2;
}
//如果你写了有参或者无参构造函数,那么编译器也不会提供无参构造函数
Test(int _a, int _b)
{
cout<<"我是copy构造函数"<<endl;
//a = 1;
//b = 2;
}
protected:
private:
int a ;
int b;
};
void main()
{
Test t1;
//Test t2;
//Test t3 = t2;
system("pause");
}
多个对象初始化问题研究
1.成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
#include "iostream"
using namespace std;
class A
{
public:
A(int _a1)
{
a1 = _a1;
std::cout << "调用了" << std::endl;
}
~A()
{
std::cout << "A析构了" << std::endl;
}
protected:
private:
int a1;
};
//构造函数的初始化列表产生原因
//语法现象
class B
{
public:
B() :mya(12), mya2(100)
{
;
}
//成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
B(int x, int y) :mya(y), mya2(101)
{
b1 = x; //这样先析构B再析构A ,和创建相反
}
~B()
{
std::cout << "析构了B" << std::endl;
}
protected:
private:
int b1;
A mya2;
A mya;
};
void k()
{
A a1(10);
B b1(10, 20);
}
void main()
{
k();
system("pause");
}
1 C++中提供了初始化列表对成员变量进行初始化
2 使用初始化列表出现原因:
1.必须这样做:
如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,
而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,
如果没有初始化列表,那么他将无法完成第一步,就会报错。
2、类成员中若有const修饰,必须在对象初始化的时候,给const int m 赋值
当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,
因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。
//总结 构造和析构的调用顺序
#include "iostream"
using namespace std;
class ABC
{
public:
ABC(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
printf("a:%d,b:%d,c:%d \n", a, b, c);
printf("ABC construct ..\n");
}
~ABC()
{
printf("a:%d,b:%d,c:%d \n", a, b, c);
printf("~ABC() ..\n");
}
protected:
private:
int a;
int b;
int c;
};
class MyD
{
public:
MyD():abc1(1,2,3),abc2(4,5,6),m(100)
{
cout<<"MyD()"<<endl;
}
~MyD()
{
cout<<"~MyD()"<<endl;
}
protected:
private:
ABC abc1; //c++编译器不知道如何构造abc1
ABC abc2;
const int m;
};
int run()
{
MyD myD;
return 0;
}
int main()
{
run();
system("pause");
return 0;
}
强化
//对象做函数参数
//1 研究拷贝构造
//2 研究构造函数,析构函数的调用顺序
//总结 构造和析构的调用顺序
#include "iostream"
using namespace std;
class ABCD
{
public:
ABCD(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
printf("ABCD() construct, a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
~ABCD()
{
printf("~ABCD() construct,a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
int getA()
{
return this->a;
}
protected:
private:
int a;
int b;
int c;
};
class MyE
{
public:
MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
{
cout<<"MyD()"<<endl;
}
~MyE()
{
cout<<"~MyD()"<<endl;
}
MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
{
printf("MyD(const MyD & obj)\n");
}
protected:
//private:
public:
ABCD abcd1; //c++编译器不知道如何构造abc1
ABCD abcd2;
const int m;
};
int doThing(MyE mye1)
{
printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA());
return 0;
}
int run2()
{
MyE myE;
doThing(myE);
return 0;
}
//
int run3()
{
printf("run3 start..\n");
ABCD abcd = ABCD(100, 200, 300);
//若直接调用构造函数哪
//想调用构造函数对abc对象进行再复制,可以吗?
//在构造函数里面调用另外一个构造函数,会有什么结果?
//ABCD(400, 500, 600); //临时对象的生命周期
printf("run3 end\n");
return 0;
}
int main()
{
//run3();
run2();
system("pause");
return 0;
}
构造函数调用构造函数将会产生一个匿名对象
#include "iostream"
using namespace std;
class MyTest
{
public:
MyTest(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
MyTest(int a, int b)
{
this->a = a;
this->b = b;
MyTest(a, b, 100);//在构造函数中调用构造函数将产生一个匿名对象,因为没有对象初始化,
}
~MyTest()
{
printf("MyTest~:%d, %d, %d\n", a, b, c);
}
protected:
private:
int a;
int b;
int c;
public:
int getC() const { return c; }
void setC(int val) { c = val; }
};
int main()
{
MyTest t1(1, 2);
printf("c:%d", t1.getC()); //请问c的值是?
system("pause");
return 0;
}
结果:大家会发现C的值是一个垃圾值,即未被初始化为0
这是因为MyTest构造函数再调用构造函数,而再次调用的构造函数无对象初始化,将产生一个匿名对象,c被赋值为100是匿名对象的c,与本身的对象无任何
关联