1) 解释一下拷贝构造函数和赋值运算符重载的作用
拷贝构造函数的作用概念:拷贝构造函数是一种特殊的构造函数,是用来复制对象的。又叫做复制
构造函数。它的形式通常是ClassName(const ClassName& other),其中other是要复制的对象。
以下3种情况会调用拷贝构造:
1.使用一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式给函数参数传值
3.以值方式返回局部对象
代码如下:
class Person {
public:
int age;
Person()
{
cout << "无参构造" << endl;
}
Person(int age)
{
cout << "调用有参构造" << endl;
this->age = age;
}
Person(const Person& other)//拷贝构造函数的参数必须是引用(避免陷入无限递归)
//否则当p初始化other时,参数是以值的形式传递调用拷贝构造,传参时在参数这里反复调用自己,陷入递归
//const:1.万能引用,既能接受左值又能就接受右值2.在实参初始化形参时,形参被const修饰避免通过形参修改实参
{
cout << "调用拷贝构造" << endl;
this->age = other.age;
}
~Person()
{
cout << "析构函数" << endl;
}
};
void fun(Person p)//参数以值的形式传递的时候会调用拷贝构造
{
}
Person fu()
{
Person p;
return p;//p在函数里面是局部变量,(函数结束后p消失)要想在外面把p返回出来,中间要拷贝一个匿名对象p'(没有名字,只存在于当前行)
//当前行结束匿名对象被释放,不是主函数结束才释放匿名对象
}
Person& f() //不让返回值执行拷贝构造 //函数返回值是右值,加上引用&函数返回值便是左值
//函数结束后p不会被销毁,所以不用拷贝匿名对象,便没有调用拷贝构造
{
Person p;
return p;
}
void test03()//返回值
{
fu();
}
void test04()
{
f();
}
void test05()
{
Person p1 = fu();//只调用一次拷贝构造(正常是两次p-p',p'-p1),但是编译器优化掉了一次
}
int main()
{
Person p;
Person p1 = p;//用已经存在的对象p来初始化新的对象p1,隐式转换法调用拷贝构造
//输出结果:无参构造,调用拷贝构造 //explicit关键字可以避免函数隐式转换法调用
//也就是说构造函数前有explicit关键字,这里就无法用隐式转换法调用
Person p2 = 3;//隐式转换法调用有参构造
//结果:无参构造,调用有参构造
Person p3;
p3 = p;//不是初始化而是赋值,所以不调用拷贝构造
fun(p);//值传递的方式给函数参数传值会调用拷贝构造(实参初始化形参)
//输出结果:无参构造,调用拷贝构造
//如果是void fun(Person& p)则不会调用拷贝构造,因为引用只是给变量起别名并没有产生新的对象
test03();
//输出结果:无参构造,调用拷贝构造,析构函数(p析构),析构函数(匿名对象p'析构)
test04();
//输出结果:无参构造,析构函数(p析构)
return 0;
}
拷贝构造又分为浅拷贝和深拷贝
浅拷贝: (简单的赋值拷贝操作) 两个对象里面的指针成员变量指向同一块堆区内存,会造成同一块堆
区内存被重复释放(析构函数被调用两次) (没有指针变量并且不指向堆区内存) 编译器默认提供的是
浅拷贝
深拷贝:(有指针变量并且指向堆区内存)指向不同的地址,但内容大小相同 (在堆区重新申请空
间,进行拷贝操作)
代码如下:
class Person {
int age;
int* p;
public:
Person()
{
cout << "调用无参构造" << endl;
age = 0;
p = nullptr;
}
Person(int age)
{
this->age = age;
this->p = new int[age];
for (int i = 0; i < age; i++)
{
p[i] = i;
}
cout << "调用有参构造" << endl;
}
Person(const Person& other)
{
cout << "调用拷贝构造" << endl;
this->age = other.age;
this->p = other.p;//浅拷贝
this->p = new int[other.age]; //保证大小相同 //深拷贝
for (int i = 0; i < age; i++)
{
this->p[i] = other.p[i];//保证内容相同
}
}
~Person()
{
cout << "调用析构函数" << endl;
if (p) delete[]p;
}
};
int main()
{
Person p(3);
Person p1(p);
return 0;
}
赋值运算符:赋值运算符简单来说就是浅拷贝,编译器会提供默认的赋值运算符,就是成员变量的
赋值操作
赋值运算符重载允许自定义对象赋值的操作,默认情况下,C++ 提供了浅赋值操作,但在许多情
况下,这种默认操作可能不符合要求,特别是当类中有指针成员时。重载后的赋值运算符形式通常
是ClassName& operator=(const ClassName& other)。
class person {
string name;
int age;
int* password;
public:
person() //构造函数
{
password = new int(3);
}
~person() //析构函数
{
if (password) delete password;
}
void operator=(const person& other)
{
this->name = other.name;
this->age = other.age;
this->password = new int(*other.password);//深拷贝
//不能写成浅拷贝this->password=other.password
//两个对象里面的指针成员变量指向同一块堆区内存,会造成同一块堆区内存被重复释放
}
};
int main()
{
person p1, p2;
p1 = p2;//赋值调用赋值运算符
return 0;
}
2)什么是c++模板,有哪些类型的模板
C++ 模板的概念:C++ 模板是一种泛型编程机制,它允许编写能够处理多种数据类型的代码,而
不是为每种数据类型编写重复的代码。模板提供了一种参数化类型的方式,使得代码可以在编译时
根据实际使用的数据类型进行定制。
函数模板类型
概念:函数模板是一种通用的函数定义,可以用于生成多个不同类型的具体函数。它通过模板参数
来表示可以变化的数据类型。
template<typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int result1 = add<int>(3, 5);
double result2 = add<double>(3.14, 2.71);
// 函数模板根据传入的模板参数类型(<int>和<double>)生成具体的函数
return 0;
}
类模板类型
概念:类模板是用于创建通用类的模板。它允许定义一个类的框架,其中部分成员(如成员变量、
成员函数)的类型是由模板参数确定的。
template<typename T, typename U>
class Pair {
public:
Pair(T first, U second) : m_first(first), m.second(second) {}
T getFirst() const { return m_first; }
U getSecond() const { return m_second; }
private:
T m_first;
U m_second;
};
int main() {
Pair<int, double> p(5, 3.14);
// 根据模板参数<int, double>创建了一个具体的Pair类实例
return 0;
}