目录
写在前面:本篇博客所提到的赋值构造函数等同于拷贝构造函数及复制构造函数。
构造函数是一个特殊的成员函数,
class Test2
{
public:
Test2() //无参数构造函数
{
m_a = 0;
m_b = 0;
cout<<"无参数构造函数"<<endl;
}
Test2(int a)
{
m_a = a;
m_b = 0;
}
Test2(int a, int b) //有参数构造函数 //3种方法
{
m_a = a;
m_b = b;
cout<<"有参数构造函数"<<endl;
}
//赋值构造函数 (copy构造函数) //
Test2(const Test2& obj )
{
cout<<"我也是构造函数 " <<endl;
}
public:
void printT()
{
cout<<"普通成员函数"<<endl;
}
private:
int m_a;
int m_b;
};
上面分别列举了无参构造函数,有参构造函数及赋值构造函数三种函数示例,其中,属最后一种最难,本篇不记,后篇详记。
下面来介绍着前两种函数如何调用:
无参构造函数
如果类后面定义的对象,什么都不写,就是调用无参构造函数了:
void main21()
{
Test2 t1; //调用无参数构造函数
cout<<"hello..."<<endl;
system("pause");
return ;
}
有参构造函数
调用有参构造函数,有三种方法:
//调用 调用有参数构造函数 3
void main22()
{
//1括号法
Test2 t1(1, 2); //调用参数构造函数 c++编译器自动的调用构造函数
t1.printT();
// 2 =号法
Test2 t2 = (3, 4, 5, 6, 7); // = c+对等号符 功能增强 c++编译器自动的调用构造函数
Test2 t3 = 5;
//3 直接调用构造函数 手动的调用构造函数
Test2 t4 = Test2(1, 2); //匿名对象 (匿名对象的去和留) 抛砖 ....//t4对象的初始化
//
t1 = t4; //把t4 copy给 t1 //赋值操作
//对象的初始化 和 对象的赋值 是两个不同的概念
cout<<"hello..."<<endl;
system("pause");
return ;
}
第一种方法,是括号法。如果这种方法,括号内必须写上参数,不能空着!
第二种方法,是等号(=)法。"="号操作,不再是C语言中的功能,C++进行了功能性增强。 逗号表达式是由很多式组成的,最后,一个值,是整个表达式的值。括号内只有值和逗号连接的情况下,它不管填入括号中多少个值,只以最后一个值为全部括号内的值,也只会输入如下的单值参数中,不会是多值。
Test2(int a)
{
m_a = a;
m_b = 0;
}
第三种方法,直接调用构造函数。前两种,是编译器自动帮我们调用,第三种是手工的调用。 需要强调的是,直接调用的话,会产生一个匿名对象,(匿名对象的生命周期的控制,是这里的一个难点,以后详记 ),现阶段只要明白,直接调用有参构造,完成t4的构造,就行了。
对象的初始化和对象的赋值是不同的概念!!!
Test2 t4 = Test2(1, 2); //匿名对象 (匿名对象的去和留) 抛砖 ....//t4对象的初始化
//
t1 = t4; //把t4 copy给 t1 //赋值操作
//对象的初始化 和 对象的赋值 是两个不同的概念
C++编译器提供的构造函数的调用方法为什么好?
自动调用,也就是隐式调用,按照规则(类名和成员函数名相同)调用。
那这种方法好不好呢?我们来做个对比,如果用显式的初始化类的属性及其他资源, 但是,要明白,如果显示的定义的话,就没机会进行初始化了。
显示的初始化的例子:
class Test3
{
public:
void init(int _a, int _b)
{
a = _a;
b = _b;
}
protected:
private:
int a;
int b;
};
如上,显式的初始化做好了,类没有定义构造函数(类没有提供构造函数,编译器会自动的给你提供一个默认的构造函数,析构函数、赋值(copy)构造函数同理)。显示的得自己调,总是没有编译器给给直接调来的舒服:
void main31()
{
//类没有提供构造函数,c++编译器会自动给你提供一个默认的构造函数
//类没有提供构造函数 copy构造构造函数, c++编译器会自动给程序员提供一个 默认的copy构造函数 =
Test3 t1;
int a = 10;
int b = 20;
t1.init(a, b); //显式的得自己调
cout<<"hello..."<<endl;
system("pause");
return ;
}
还有一点,如果定义的对象是个数组,如上"tArray[3]",怎么样对这个类型初始化呢?除非,依旧一个一个的进行调用。
void main31()
{
Test3 tArray[3];
tArray[0].init(1, 2);
tArray[1].init(1, 2);
tArray[2].init(1, 2);
cout<<"hello..."<<endl;
system("pause");
return ;
}
更有甚者,这样定义:
void main31()
{
Test3 t21; t21.init(1, 2);
Test3 t22; t22.init(1, 2);
Test3 t23; t23.init(1, 2);
cout<<"hello..."<<endl;
system("pause");
return ;
}
再或者,这样定义:
void main31()
{
//在这种场景之下 显示的初始化方案 显得很蹩脚
Test3 tArray2[3] = {t21, t22, t23};
cout<<"hello..."<<endl;
system("pause");
return ;
}
在这种场景之下 显示的初始化方案 显得很蹩脚。所以,编译器给我们提供的这种构造函数和析构函数的这种模式,是非常便捷的!
甚至,在这种场景下满足不了需求了:
void main31()
{
//在这种场景之下,满足不了,编程需要
Test3 tArray3[1999] = {t21, t22, t23};
cout<<"hello..."<<endl;
system("pause");
return ;
}
总之,编译器通过构造和析构函数这种隐式调用模式,是非常爽的!!!!
总体代码
dm02_构造函数的分类.cpp
#include <iostream>
using namespace std;
class Test2
{
public:
Test2() //无参数构造函数
{
m_a = 0;
m_b = 0;
cout<<"无参数构造函数"<<endl;
}
Test2(int a)
{
m_a = a;
m_b = 0;
}
Test2(int a, int b) //有参数构造函数 //3种方法
{
m_a = a;
m_b = b;
cout<<"有参数构造函数"<<endl;
}
//赋值构造函数 (copy构造函数) //
Test2(const Test2& obj )
{
cout<<"我也是构造函数 " <<endl;
}
public:
void printT()
{
cout<<"普通成员函数"<<endl;
}
private:
int m_a;
int m_b;
};
void main21()
{
Test2 t1; //调用无参数构造函数
cout<<"hello..."<<endl;
system("pause");
return ;
}
//调用 调用有参数构造函数 3
void main22()
{
//1括号法
Test2 t1(1, 2); //调用参数构造函数 c++编译器自动的调用构造函数
t1.printT();
// 2 =号法
Test2 t2 = (3, 4, 5, 6, 7); // = c+对等号符 功能增强 c++编译器自动的调用构造函数
Test2 t3 = 5;
//3 直接调用构造函数 手动的调用构造函数
Test2 t4 = Test2(1, 2); //匿名对象 (匿名对象的去和留) 抛砖 ....//t4对象的初始化
//
t1 = t4; //把t4 copy给 t1 //赋值操作
//对象的初始化 和 对象的赋值 是两个不同的概念
cout<<"hello..."<<endl;
system("pause");
return ;
}
dm03_显示初始化方案.cpp
#include <iostream>
using namespace std;
class Test3
{
public:
void init(int _a, int _b)
{
a = _a;
b = _b;
}
protected:
private:
int a;
int b;
};
void main31()
{
//类没有提供构造函数,c++编译器会自动给你提供一个默认的构造函数
//类没有提供构造函数 copy构造构造函数, c++编译器会自动给程序员提供一个 默认的copy构造函数 =
Test3 t1;
int a = 10;
int b = 20;
t1.init(a, b);
Test3 tArray[3];
tArray[0].init(1, 2);
tArray[1].init(1, 2);
tArray[2].init(1, 2);
//
Test3 t21; t21.init(1, 2);
Test3 t22; t22.init(1, 2);
Test3 t23; t23.init(1, 2);
//在这种场景之下 显示的初始化方案 显得很蹩脚
Test3 tArray2[3] = {t21, t22, t23};
//在这种场景之下,满足不了,编程需要
Test3 tArray3[1999] = {t21, t22, t23};
cout<<"hello..."<<endl;
system("pause");
return ;
}