C++Day3
构造函数与析构函数的语法
-
构造和析构必须要声明在全局作用域
-
//构造函数 //构造函数的任务是初始化类对象的数据成员。只要类的对象被创建,就会执行构造函数 //没有返回值,不用写void //函数名与类名相同 //可以有参数,可以发送重载 //构造函数由编译器自动调用一次,无须手动调用 //构造函数不能被声明为const的。当创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其“常量”属性。因此函数在const对象的构造过程中可以向其写值 class Person { public: Person() { } //析构函数 //没有返回值,不用写void //函数名与类目相同,函数名前加~ //不可以有参数,不可以发生重载 //析构函数由编译器自动调用一次,无需手动调用 ~Person() { } } void test01() { Person p;//调用出构造函数与析构函数 }
构造函数的分类以及调用
-
构造函数分类
-
按参数
-
无参构造(默认构造函数)
-
类通过默认构造函数控制默认初始化过程
-
//默认构造函数 Person()=default; Person1() { } //可以在参数列表后添加==default要求编译器生成默认构造函数 //=default即可以和声明一起出现在类内,也可以作为定义出现在类外
-
-
有参构造
-
class Person { public: Person(int a) { } } void test01() { Person p(1); }
-
-
按类型
-
普通构造函数
-
拷贝构造函数
-
class Person { public: Person() { cout<<"调用"<<endl; } Person(int a) { m_Age=a; } //拷贝构造函数 Person(const Person &p) { m_Age=p.m_Age; } int m_Age; } int main() { Person p(18); Person p2(p); cout<<"p2的年龄:"<<p2.m_Age<<endl; }
-
-
-
构造函数调用
-
void test01() { Person p; //括号法 Person p1(10); Person p2(p); //Person p3();这样会被认为是函数声明而不是函数调用 //不要用括号法调用无参构造函数 //显示法 Person p3=Person(10);//有参构造 Person p4=Person(p3);//拷贝构造 //匿名对象 Person(10); //特点:当前行执行完后立刻释放 //不可用拷贝构造函数初始化匿名对象 //Person(p3);//此时会被认为重定义,与Person p3相同 //隐式法 Person p5=10;//Person p5=Person(10) Person p6=p5;//Person p6=Person(p5) }
-
-
合成的默认构造函数:编译器创建的构造函数
-
某些类不能依赖于合成的默认构造函数
- 只有当类没有声明任何构造函数时,编译器才会自动生成默认构造函数
- 如果类包含内置类型或复合类型的成员,只有当这些成员全都被赋予了类内的初始值,这个类才适合使用合成的默认构造函数
- 编译器无法为某些类合成默认的构造函数。如类中包含一个其他类类型的成员且此成员的类型没有默认构造函数
-
如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数
拷贝构造函数的调用时机
//用已经创建好的对象来初始化新的对象
void test01()
{
Person p1(18);
Person p2=Person(p1);
}
//值传递的方式给函数的参数传值
void doWork(Person p)
{
}
void test02()
{
Person P1(100);
doWork(p1);
}
//以值的方式返回局部对象
Person doWork2()
{
Person p;
return p;
}
void test03()
{
Person p=doWork2();
}
构造函数的调用规则
- 编译器会给一个类 至少添加3个函数 默认函数(空实现) 析构函数(空实现) 拷贝构造(值拷贝)
- 如果自己提供了有参构造函数 编译器就不会提供默认构造函数 但是仍然会提供拷贝构造函数
- 如果自己提供拷贝构造函数,编译器不会提供其他构造函数
深浅拷贝问题及解决
class Person
{
public:
Person(const char * name,int age)
{
m_Name=(char *)malloc(strlen(name)+1);
strcpy(m_Name,name);
m_Age=age;
}
/*~Person()
{
if(m_Name!=NULL)
{
free(m_Name);
m_Name=NULL;
}
}*///p与p2会重复释放出错,应该先释放p2在释放p
char * m_Name;//姓名
int m_Age;//年龄
};
void test01()
{
Person p("dd",18);
cout<<"姓名:"<<p.m_Name<<"年龄:"<<p.m_Age<<endl;
Person p2(p);
cout<<"姓名:"<<p2.m_Name<<"年龄:"<<p2.m_Age<<endl;
}
int main()
{
test01();
}
-
利用深拷贝解决浅拷贝问题
-
class Person { public: Person(const char * name,int age) { m_Name=(char *)malloc(strlen(name)+1); strcpy(m_Name,name); m_Age=age; } Person(const Person&p) { m_Name=(char*)malloc(strlen(p.m_Name)+1); strcpy(m_Name,p.m_Name); } ~Person() { if(m_Name!=NULL) { free(m_Name); m_Name=NULL; } } char * m_Name;//姓名 int m_Age;//年龄 }; void test01() { Person p("dd",18); cout<<"姓名:"<<p.m_Name<<"年龄:"<<p.m_Age<<endl; Person p2(p); cout<<"姓名:"<<p2.m_Name<<"年龄:"<<p2.m_Age<<endl; } int main() { test01(); }
构造函数初始化列表
class Person
{
public:
Person():m_A(10),m_B(20),m_C(30)
{
}
int m_A;
int m_B;
int m_C;
};
Sales_data(const std::string &s):bookNo(s){}
Sales_data(const std::string *s):bookNo(s),units_sold(0),revenue(0){}
//二者相同 当某个数据成员被构造函数初始值列表忽略时,其将会与合成默认构造函数相同的方式隐式初始化
class Person
{
public:
Person(int a,int b,int c):m_A(a),m_B(b),m_C(c)
{
}
int m_A;
int m_B;
int m_C;
};
void test()
{
Person p(10,20,30);
}
//一般避免使用某些成员初始化其他成员
class X
{
public:
int i;
int j;
X(int val):j(val),i(j){}
//实际上是i先被初始化
}
在类的外部定义构造函数
Sale_data::Sale_data(std::istream &is)
{
read(is,*this);//read函数的作用时从is中读取一条信息然后存入this对象中
}
//在类的外部定义构造函数时必须指明是哪个类的成员
//尽管构造函数初始值列表是空的,但是由于执行了构造函数体,所以对象成员能够被初始化
类对象做类中成员
class Phone
{
public:
Phone(string pName)
{
cout<<"Phone的有参函数调用"<<endl;
m_PhoneName=pName;
}
~Phone()
{
cout<<"Phone的析构函数调用"<<endl;
}
string m_PhoneName;
};
class Game
{
public:
Game(string gName)
{
cout<<"Game的有参函数调用"<<endl;
m_GameName=gName;
}
~Game()
{
cout<<"Game的析构函数调用"<<endl;
}
string m_GameName;
};
class Person
{
public:
Person(string name,string pName,string gName):m_Name(name),m_Phone(pName),m_Game(gName)
{
cout<<"Person的有参函数调用"<<endl;
}
void PlayGame()
{
cout<<m_Name<<"拿着"<<"<<m_Phone.m_PhoneName<<”<<"牌手机,玩着:"<<m_Game.m_GameName<<endl;
}
~Person()
{
cout<<"Person的析构函数调用"<<endl;
}
string m_Name;
Phone m_Phone;
Game m_Game;
};
void test01()
{
//当其他类对象作为本类,先构造其他类对象按,析构顺序和构造相反,先进栈后出
Person p("张三","苹果","王者荣耀");
}
explicit关键字
class MyString
{
public:
MyString(char *len)
{
}
//explicit:防止利用隐式类型转换方式来构造对象
explicit MyString(int len)
{
}
};
void test01()
{
MyString str1(10);
MyString str2=MyString(100);
MyString str3=10;//"10"
}
new和delete使用
class Person
{
pbulic:
Person()
{
cout<<"Person构造函数调用"<<endl;
}
~Person()
{
cout<<"Person析构函数调用"<<endl;
}
};
void test01()
{
Person * p=new Person;
delete p;
}
//不要用void*去接受new出来的对象,因为无法释放(调用析构函数)
//利用new开辟数组
void test03()
{
//int * pInt=new int[10];
//double *pD=new double[10];
//在堆区开辟数组,一定会调用构造函数
Person * pPerson=new Person[10];
//释放数组时需要加[]
delete[] pPerson;
//栈上开辟数组,可以没有默认构造函数
Person pArray[10]={Person(10),Person(20)};
}
- malloc和free属于库函数
- new和delete属于运算符
- malloc不会调用构造函数 new会
- malloc返回void* C++下要强转 new返回创建的对象的指针