C++基础学习三

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

C++基础学习三

  • 一,构造
    • 1 构造函数的分类(构造和析构必须在public下才可以调用)
    • 2 拷贝构造函数调用时机
    • 3 构造函数调用规则
    • 4 深拷贝和浅拷贝
    • 5 初始化列表
    • 6 类对象作为类成员
    • 7 explicit关键字
  • 二、new


一,构造

构造函数和析构函数这两个函数是被编译器自动调用的。
无论你是否喜欢,对象的初始化和清理工作都是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事情,

class Person
{
public:
	//构造函数写法
	//与类名相同,没有返回值,不写void,可以发生重载(可以有参数),编译器自动调用,且只调用一次
	Person()
	{
	}
	//析构函数写法
	//与类名相同,类名前面加一个~,没有返回值,不写void,不可以发生重载,自动调用,且一次
	~Person()
	{
	}
}

1 构造函数的分类(构造和析构必须在public下才可以调用)

按照参数进行分类,无参构造函数,有参构造函数
按照类型进行分类, 普通构造函数,拷贝构造函数

class Person
{
public:
	//构造函数写法
	//与类名相同,没有返回值,不写void,可以发生重载(可以有参数),编译器自动调用,且只调用一次
	Person()
	{
		cout<<"默认构造"<<endl;
	}
	Person(int a)
	{
		cout<<"有参构造"<<endl;
	}
	Person(const Person &p)//不能修改被拷贝的值
	{
		cout<<"有参构造"<<endl;
	}
	//析构函数写法
	//与类名相同,类名前面加一个~,没有返回值,不写void,不可以发生重载,自动调用,且一次
	~Person()
	{
	}
}

Person p1(1); //有参
Person p2(p1); //拷贝
Person p3;  //默认构造函数不要加()
Person p3(); //编译器认为这是函数声明,返回值为Person,函数名为p3的函数声明

Person p4 = Person(100); //显示调用有参
Person p5 = person(p4);
Person(100) //叫匿名对象,匿名对象特点,如果编译器发现对象是匿名的,那么在这行代码的结束胡就释放这个对象

//不能用拷贝构造函数初始化匿名对象
Person(p5)//如果写成左值,编译器认为写成Person p5,与上面的p5重定义。如果写成Person p6 = Person(p5)可以

Person p7 = 100; //相当于Person p7 = Person(100),隐式类型装换
Person p8 = p7;

2 拷贝构造函数调用时机

1.用一个已经创建好 的对象初始化新的对象
2.以值传递的方式给函数参数传值
例如

void func(Person p) //Person p = Person(p1) 所以建议使用引用,节省开销
{
}
Person p1;
func(p1);

3.以值的方式返回局部对象

Person func2() //不能返回局部对象的引用,可以以值方式返回局部对象
{
	Person p1;
	return p1;
}
Person p = func(); //debug模式下会输出默认和拷贝两个构造
/*release版本只会出现一次默认构造,编译器将代码修改如下
Person p;不调用默认构造
func2(p);
void func2(Person &p)
{
	Person p1 //调用默认构造
}

*/

3 构造函数调用规则

1.系统会默认给一个类提供3个函数,默认构造 ,拷贝构造、析构函数
2.当我们提供了有参的构造函数,那么系统就会再提供默认构造函数了,但是系统还是会提供默认拷贝构造函数
3.当我们提供了拷贝构造,系统就不会提供其他构造了


4 深拷贝和浅拷贝

如果属性里有指向堆区空间的数据,那么简单的浅拷贝会导致重复释放内存的异常

class Person
{
public:

	Person()
	{}
	//初始化属性
	Person(char * name,int age)
	{
		m_Name = (char*)malloc(strlen(name) + 1);
		strcpy(m_Name, name);

		m_age = age;
	}
	//拷贝构造 系统会提供默认拷贝构造,而且是简单的值拷贝
	//自己提供拷贝构造,原因简单的浅拷贝会释放堆区空间两次,导致挂掉
	//深拷贝
	Person(const Person&p)
	{
		m_age = p.m_age;
		m_Name = (char*)malloc(strlen(p.m_Name) + 1);
		strcpy(m_Name, p.m_Name);
	}

	~Person()
	{
		cout << "析构函数调用" << endl;
		if (m_Name!=NULL)
		{
			free(m_Name);
			m_Name = NULL;
		}
	}


	//姓名
	char * m_Name;
	//年龄
	int m_age;
};


void test01()
{
	Person p1("敌法",10);
	Person p2(p1); //调用拷贝构造

	
}

5 初始化列表

class Person
{
public:

	//有参构造初始化数据
	/*Person( int a,int b,int c)
	{
		m_A = a;
		m_B = b;
		m_C = c;
	}*/

	Person() :m_A(10), m_B(20), m_C(30)
	{}

	//利用初始化列表 初始化数据
	// 构造函数后面  +  : 属性(参数), 属性(参数)...
	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 test01()
{
	Person p1(10, 20, 30);

	cout << "p1的m_A :" << p1.m_A << endl;
	cout << "p1的m_B :" << p1.m_B << endl; 
	cout << "p1的m_C :" << p1.m_C << endl;

	Person p2;
	cout << "p2的m_A :" << p2.m_A << endl;
	cout << "p2的m_B :" << p2.m_B << endl;
	cout << "p2的m_C :" << p2.m_C << endl;
}

6 类对象作为类成员

类对象作为类成员时,构造顺序先将类对象一一构造,然后构造自己,析构的顺序是相反的

class Phone
{
public:
	Phone()
	{
		cout << "手机的默认构造函数调用" << endl;
	}
	Phone(string name)
	{
		cout << "手机的有参构造调用" << endl;
		m_PhoneName = name;
	}
	
	~Phone()
	{
		cout << "手机的析构函数调用" << endl;
	}

	string m_PhoneName;

};

class Game
{
public:
	Game()
	{
		cout << "Game的默认构造函数调用" << endl;
	}
	Game(string name)
	{
		cout << "Game的有参构造调用" << endl;
		m_GameName = name;
	}
	~Game()
	{
		cout << "Game的析构函数调用" << endl;
	}

	string m_GameName;

};

class Person
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}

	Person(string name, string phoneName, string gameName) : m_Name(name), m_Phone(phoneName), m_Game(gameName)
	{
		cout << "Person的有参构造调用" << endl;
		//m_Name = name;
	}

	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("狗蛋","苹果","切水果");
	p.playGame();
}

7 explicit关键字

class MyString
{
public:

	MyString(const char * str)
	{
		//
	}

	explicit MyString(int a)
	{
		mSize = a;
	}

	char* mStr;
	int mSize;
};

void test01()
{
	
	MyString str = "abc";
	MyString str2(10);
	//MyString str3 = 10; //做什么用图? str2字符串为 "10" 字符串的长度10 
	//隐式类型转换  Mystring str3 = Mystring (10);
	// explicit关键字 ,防止隐式类型转换

}

二、new

malloc申请的内存可能会失败,且申请后需要初始化。
所有new出来的对象都会返回该类型的指针,mallo返回void *
new和delete是运算符,malloc是函数,配合free使用
通过new创建数组,一定为会调用默认构造,所以一定要有默认构造

class Person
{
public:
	Person()
	{
		cout << "默认构造调用" << endl;
	}

	Person(int a)
	{
		cout << "有参构造调用" << endl;
	}

	~Person()
	{
		cout << "析构函数调用" << endl;
	}

};

void test01()
{
	//Person p1;  栈区开辟

	Person * p2 = new Person; //堆区开辟

	//所有new出来的对象 都会返回该类型的指针
	//malloc 返回 void* 还要强转
	//malloc会调用构造吗? 不会  new会调用构造
	// new 运算符  malloc 函数
	//释放 堆区空间
	// delete也是运算符 配合 new用  malloc 配合 free用
	delete p2;

}

void test02()
{
	void *p = new Person(10); 
	//当用void* 接受new出来的指针 ,会出现释放的问题
	delete p;
	//无法释放p ,所以避免这种写法
}

void test03()
{
	//通过new开辟数组 一定会调用默认构造函数,所以一定要提供默认构造
	Person * pArray = new Person[10];
	//Person pArray2[2] = { Person(1), Person(2) }; //在栈上开辟数组,可以指定有参构造


	//释放数组 delete [] 编译器会给堆上的数组一个数组大小的记录,如果不加[],delete就不会去找这个记录
	delete [] pArray;
}

你可能感兴趣的:(C/C++,c++)