构造函数的定义
构造函数是用于对象生成时,为对象的成员变量赋初值的地方。只要有新对象生成就会调用它。
构造函数是没有返回值的,所以定义构造函数时只需要使用类名作为构造函数名前面用访问修饰符修饰即可。
如果用户没有在类中自己定义构造函数的话,在生成对象的时候编译器会自动调用一个默认构造函数,这个默认构造函数中不包含任何语句。一旦用户自己定义构造函数时默认构造函数就不存在了。
#include<iostream>
using namespace std;
class A
{
};
class B
{
public:
B(int n)
{
}
};
int main()
{
A a;
B b;
system("pause");
return 0;
}
错误提示:
可以看出:
在类A内我没有定义构造函数,在生成对象时成功通过,类B我定义了一个含int参数的构造函数结果在生成类B的对象时就失败了,提示没有合适的默认构造函数,因为已经自己定义构造函数了所以默认提供的构造函数就没用了所以无法成功生成。
构造函数种类
无参构造函数
顾名思义无参构造函数就是不含参数的构造函数,编译器提供的默认构造函数就是无参构造函数,当然我们也可以自己定义无参构造函数。
#include<iostream>
using namespace std;
class MyData
{
public:
MyData()
{
_year = 2000;
_month = 1;
_day = 1;
}
void show()
{
cout << _year << ":" << _month << ":" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
MyData data1;
data1.show();
system("pause");
return 0;
}
可以看出:
在我们自己定义无参构造函数后,使用MyData data1;这种形式直接生成对象时就会调用我们自己的无参构造函数,这样可以解决因生成对象时未对成员变量赋初值而直接访问导致的错误。
一般构造函数
一般构造函数是我们最常用的构造函数形式,也就是构造函数各种类型的重载。
#include<iostream>
using namespace std;
class MyData
{
public:
MyData(int year):_month(1),_day(1)
{
_year = year;
}
MyData(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
void show()
{
cout << _year << ":" << _month << ":" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
MyData data1(2016);
data1.show();
MyData data2(2016,8,15);
data2.show();
system("pause");
return 0;
}
拷贝构造函数
也是构造函数重载的一种,特殊的是它的参数是类对象的引用,他会将实参的对象的值复制一份给新生成的对象,编译器默认提供拷贝构造函数,但如果类成员变量中含有指针类型则建议自己定义拷贝构造函数。
因为默认提供的拷贝构造函数是浅拷贝,对于指针变量只会单纯复制一份地址并不会把指针指向内存的内容也复制一份,就会造成两个对象的指针变量指向同一块内存,导致操作结果相互影响。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
using namespace std;
class MyString
{
public:
//重载构造函数
MyString(int len)
{
_len = len;
_data = new char(_len * sizeof(char));
}
void set(char *str)
{
if (_len >= (sizeof(str) + 1))
strcpy(_data, str);
}
char* get()
{
return _data;
}
private:
char *_data;
int _len;
};
int main()
{
MyString str1(5);
str1.set("1234");
//通过str1拷贝构造str2
MyString str2(str1);
//输出str1
cout << str1.get() << endl;
//输出str2
cout << str2.get() << endl;
//修改str2
str2.set("5678");
//输出str1
cout << str1.get() << endl;
//输出str2
cout << str2.get() << endl;
system("pause");
return 0;
}
可以看出:
str2改变自身字符串的同时str1的字符串也变化了。
即:使用默认的拷贝构造函数生成的对象指针成员变量指向内存的值,会随着拷贝源对象指针成员变量指向内存的值的变化而变化。
所以:如果类成员变量中含有指针,需要自己重写拷贝构造函数以解决浅拷贝导致的问题。
转换构造函数
也叫类型转换构造函数,只需要传入一个参数且参数不为本类的const引用(参数为本类的const引用的叫拷贝构造函数)的构造函数就是转换构造函数。
使用方式有以下几种:
- 类名 对象名=参数;
- 类名 对象名(参数);
- 类名 对象名= 类名(参数);
- 类名 对象名= (类名)参数;
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
using namespace std;
class MyString
{
public:
//重载构造函数
MyString(char *str)
{
_len = strlen(str) + 1;
_data = new char(_len);
strcpy(_data, str);
}
void show()
{
cout << _data << endl;;
}
private:
char *_data;
int _len;
};
int main()
{
//类名 对象名=参数;
MyString str1="1111";
str1.show();
//类名 对象名(参数);
MyString str2("2222");
str2.show();
//类名 对象名= 类名(参数);
MyString str3 = MyString("3333");
str3.show();
//类名 对象名= (类名)参数;
MyString str4 =(MyString) ("4444");
str4.show();
system("pause");
return 0;
}
这里的只需传入一个参数并不意味着只能有一个参数,还可以这样:
MyString(char *str, int=1);
这里虽然有两个参数,但其中一个参数已经提供了默认值我传入一个也可以所以这样的也是转换构造函数。
其他
转化构造和赋值的区别
转换构造:MyString str1=”1111”;
赋值:MyString str1;str1=”1111”;
转换构造的具体情况上面已经讲得差不过多了。这里就不再说了着重介绍赋值的情况;
首先像str1=”1111”;这样的赋值并不属于构造函数即使它在执行的过程中使用了构造函数。
#include<iostream>
using namespace std;
class MyClass
{
public:
MyClass(int n)
{
cout << "调用了构造函数" << endl;
}
~MyClass()
{
cout << "调用了析构函数!" << endl;
}
};
void main()
{
MyClass s1(2);//转换构造
s1 = 3; //赋值
}
单步调试查看输出
首先是转换构造
可以看出:MyClass s1(2); 调用了转换构造函数生成s1对象。
然后是赋值
这里就很奇怪了,我只是给对象做了一下赋值操作,为什么先后调用了构造函数和析构函数呢?
这是因为在给对象A赋b值时,编译器会先调用构造函数通过b值生成一个临时对象C,然后再把这个临时对象C赋值给对象A,之后再销毁对象C。(要注意此时临时对象C与对象A之间只是调operator=(class&),并不属于拷贝构造因为此时并没有生成新对象,A和C均是已经生成好的对象)。
对于类new和malloc区别
new和malloc都是在堆分配内存,不同的是对于对象来说通过new分配内存时会调用构造函数,而malloc则不会;
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{
cout << "调用了构造函数" << endl;
}
void set(char *name)
{
strcpy(_name,name);
}
void show()
{
cout << _name << endl;;
}
private:
char _name[20];
};
void main()
{
//new方式分配内存
cout << "new方式分配内存" << endl;
MyClass *s1=new MyClass();
s1->set("s1");
s1->show();
//malloc方式分配内存
cout << "\nmalloc方式分配内存" << endl;
MyClass *s2 = static_cast<MyClass *>(malloc(sizeof(MyClass)));
s2->set("s2");
s2->show();
system("pause");
}
很明显只有new方式的时候调用了构造函数。