面向对象程序设计(1)this指针、构造函数、析构函数

目录

1,this指针

2,六个默认函数

3,构造函数 Constructor

(1)默认构造函数 Default constructor

(2)自定义构造函数

(3)拷贝构造函数 Copy constructor

(4)移动构造函数 Move constructor

4,赋值运算符

(1)拷贝赋值运算符 Copy assignment operator

(2)移动赋值运算符 Move assignment operator

5,析构函数 Destructor

6,构造函数初始化列表


1,this指针

this是一个C++内置的特殊指针,用来指向引用当前函数的调用对象。

2,六个默认函数

类中有六个特殊函数(运算符):

Default constructor
Copy constructor
Copy assignment operator
Move constructor (since C++11)
Move assignment operator (since C++11)
Destructor

3个构造函数,2个运算符,1个析构函数。

3,构造函数 Constructor

在创建对象时,构造函数被调用。

构造函数的名字必须和类名相同,没有返回类型(包括void也没有)。

构造函数可以重载。

(1)默认构造函数 Default constructor

如果没有构造函数,那么编译器会自动生成一个默认构造函数。

如果自定义了任何一种构造函数,包括自定义构造函数、自定义拷贝构造函数,自定义移动构造函数,那么都不会自动生成默认构造函数。

#include<iostream>
using namespace std;

class A
{
    int x=3;
public:
    void out()
    {
        cout<<x<<"  ";
    }
};

int main()
{
    A a;
    a.out();
    return 0;
}

ps:默认和自动生成的概念有所差别,尤其是有了delete和default关键字之后更加明显。

(2)自定义构造函数

#include <iostream>
using namespace std;

class MyClass
{
public:
    int x,y;
    MyClass()
    {
        x=1,y=2;
    }
    MyClass(int x)
    {
        this->x=x,y=2;
    }
    void out()
    {
        cout<<x<<y<<endl;
    }
};

int main() {
    MyClass cs;
    cs.out();
    MyClass cs2(3);
    cs2.out();
    return 0;
}

(3)拷贝构造函数 Copy constructor

拷贝构造函数用来创建一个对象,并用另一个对象的数据初始化新建对象。
默认的拷贝构造函数,和用'='完成赋值一样,都是浅拷贝。
要想完成深拷贝,需要自定义拷贝构造函数。

(3.1)正则拷贝构造函数

正则拷贝构造函数的入参只有一个,就是本类的对象

#include <iostream>
using namespace std;

class MyClass
{
public:
    int x,y;
    MyClass(int x)
    {
        this->x=x,y=2;
    }
    MyClass(MyClass &cs)
    {
        x=cs.x,y=cs.y;
    }
    void out()
    {
        cout<<x<<" "<<y<<endl;
    }
};

int main() {
    MyClass cs(9);
    MyClass cs2(cs);
    cs2.out();
    return 0;
}

必须是引用传递参数,否则会发生无限递归。因为在值传递的过程中,会调用拷贝构造函数。

(3.2)拓展拷贝构造函数

拓展拷贝构造函数的入参除了本类的对象之外,至少还有一个其他参数,

在进行值传递的时候,就不会调用该构造函数,而是直接使用默认的‘=’完成浅拷贝,因为不会发生无限递归情况,所以参数传值或者传引用都可以。

#include <iostream>
using namespace std;

class MyClass
{
public:
    int x,y;
    MyClass(int x)
    {
        this->x=x,y=2;
    }
    MyClass(int t,MyClass cs)
    {
        x=cs.x+t,y=cs.y+t;
    }
    void out()
    {
        cout<<x<<" "<<y<<endl;
    }
};

int main() {
    MyClass cs(9);
    MyClass cs2(1,cs);
    cs2.out();
    MyClass cs3(cs);
    cs3.out();
    return 0;
}

输出:

10 3
9 2

本质上,拓展拷贝构造函数其实就是自定义构造函数的一种,一般我们说的拷贝构造函数,就是指正则拷贝构造函数,本文默认同样如此。

如果自定义了正则拷贝构造函数,就不会再生成默认拷贝构造函数,因为签名是相同的。

如果只自定义了拓展拷贝构造函数,那么编译器会生成默认拷贝构造函数。

(4)移动构造函数 Move constructor

#include <iostream>
using namespace std;

class MyClass
{
public:
	int x, y;
	MyClass(int x)
	{
		this->x = x, y = 2;
	}
	MyClass(MyClass &&cs)
	{
		x = cs.x, y = cs.y;
	}
	void out()
	{
		cout << x << " " << y << endl;
	}
};

int main() {
	MyClass cs(9);
	MyClass cs2(std::move(cs));
	cs2.out();
	return 0;
}

4,赋值运算符

(1)拷贝赋值运算符 Copy assignment operator

#include <iostream>
using namespace std;

class MyClass
{
public:
    int x=2,y=3;
    MyClass & operator=(const MyClass &cs)
    {
        x=cs.x+1,y=cs.y+1;
        return *this;
    }
    void out()
    {
        cout<<x<<" "<<y<<endl;
    }
};

int main() {
    MyClass cs;
    MyClass cs2;
    cs2=cs;
    cs2.out();
    return 0;
}

输出:3 4

这就是重载了等号运算符‘=’,所以如果没有重载,还是会有默认拷贝赋值运算符,就是‘=’,是浅拷贝。

(2)移动赋值运算符 Move assignment operator

#include <iostream>
using namespace std;

class MyClass
{
public:
	int x = 2, y = 3;
	MyClass & operator=(MyClass &&cs)
	{
		x = cs.x + 1, y = cs.y + 1;
		return *this;
	}
	void out()
	{
		cout << x << " " << y << endl;
	}
};

int main() {
	MyClass cs;
	MyClass cs2;
	cs2 = std::move(cs);
	cs2.out();
	return 0;
}

5,析构函数 Destructor

在对象销毁时,析构函数被调用。
如果没有定义析构函数,那么会有一个默认的析构函数。
析构函数的名字必须是类名前加~,没有返回类型和参数。
析构函数不能被重载。

#include <iostream>
using namespace std;

class MyClass
{
public:
    int *p;
    MyClass(int n)
    {
        p=new int[n];
    }
    ~MyClass()
    {
        delete[]p;
    }
};

int main() {
    MyClass cs(10);
    return 0;
}

6,构造函数初始化列表

如果一个类A的数据域中有一个对象B,而且对象B所在类没有无实参构造函数,那么类A的构造函数就必须使用初始化列表。

#include <iostream>
using namespace std;

class MyClass
{
public:
    int x,y;
    MyClass(int x)
    {
        this->x=x,y=2;
    }
    void out()
    {
        cout<<x<<" "<<y<<endl;
    }
};

class MyClass2
{
public:
    int *p;
    int x;
    MyClass mcs;
    MyClass2(int n):mcs(n*n),x(55)
    {
        p=new int[n];
    }
    ~MyClass2()
    {
        delete[]p;
    }
    void out()
    {
        mcs.out();
        cout<<x<<endl;
    }
};

int main() {
    MyClass2 cs(9);
    cs.out();
    return 0;
}

类成员的初始化顺序,是类中的声明顺序决定的,和成员在初始化列表中的顺序无关,为了避免混淆,最好写成顺序一致。

另外一个示例:

cla.h

class Test
{
public:
	Test(int a);
	int get();
private:
	int testval;
};
class Student {
public:
	Student(int a);
	Student(Test t);
	Test test;
};

cla.cpp

#include"cla.h"

int Test::get()
{
	return this->testval;
}
Test::Test(int ina)
{
	this->testval = ina+4;
}
Student::Student(int a) : test(a)
{
}
Student::Student(Test t) : test(t)
{
}

main.cpp

#include"cla.h"
#include<iostream>
using namespace std;
int main()
{
	Student s1(2);
	Test t(3);
	Student s2(t);	
	cout << s1.test.get() << endl;
	cout << s2.test.get() << endl;
	return 0;
}

输出:

6
7

其中,Student::Student(int a) : test(a) 里面的 test(a)其实调用构造函数

而Student::Student(Test t) : test(t)里面的test(t)其实是调用拷贝构造函数

为什么构造函数初始化列表设计的时候,这里要用对象名test,而不用类名呢?

因为一个类里面可能包含多个成员,都是某个类的对象,用类名就无法区分了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值