c++基础知识复习(1)

前期知识准备

1 构造函数

在这里插入图片描述

在这里插入图片描述

(1)默认构造函数:没有参数传入,也没有在类里面声明

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(2)手动定义默认构造函数:没有参数传入,但是在类里面进行了声明

可以在类外实现或者类内实现
在这里插入图片描述
以下案例是类外实现了手动定义的默认构造函数
在这里插入图片描述
构造函数的调用:不需要手动调用,定义类对象的时候就实现了自动调用

int main()
{
   
   
	Human zhangshan; 定义对象,此处会自动调用构造函数
 ......
}

(2)自定义重载构造函数,名字相同,内容不同

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(3)拷贝构造函数

拷贝构造函数的两种调用方式
在这里插入图片描述
拷贝构造函数的定义,关键字const以及引用符号&,引用符号&后面可接变量,也可以不接
在这里插入图片描述
拷贝被造函数的类外定义
在这里插入图片描述

(a) !!!!如果不定义拷贝函数而使用了拷贝功能,c++会自动生成一个拷贝函数,实现的是浅拷贝

直接将数据拷贝过去,如果只是普通的数据类型是没有关系的,但是如果是指针,就会出现问题,以下列案例为例:
假设一开始定义了3个类,将如花拷贝给张山的girlfriend变量,也就是girlfriend指向如花
在这里插入图片描述

没有定义拷贝函数的情况下,实现的拷贝,浅拷贝,也就是不同类对象指向同一块内存地址,前面完成拷贝之后,再修改h1的值,还是导致h2,h3的值都被修改,这将导致错误。正确的逻辑是,拷贝之后,h1,h2,h3之间的值不再相互干扰除非再次执行赋值函数
#include <iostream>
#include<string.h>
#include<graphics.h>

using namespace std;
#define ADDR_LEN 64  //宏

class Human {
   
   
public:
	Human();
	Human(int salary);
	void description();
	void setAddr(char *addr);

private:
	int age = 18;
	int salary;
	char *addr;
};

void Human::description()
{
   
   
	cout << "addr = " << addr << endl;
}

Human::Human(int salary)
{
   
   
	salary = 20000;
	this->addr = new char[ADDR_LEN];  //指针需要进行初始化分配空间
	strcpy_s(this->addr, ADDR_LEN, "china");
}

Human::Human()
{
   
   
	salary = 20000;
	this->addr = new char[ADDR_LEN];  //指针需要进行初始化分配空间
	strcpy_s(this->addr, ADDR_LEN, "china");
}

void Human::setAddr(char* addr)
{
   
   
	if (!addr)
	{
   
   
		return;
	}
	strcpy_s(this->addr, ADDR_LEN, addr);
}

int main(void)
{
   
   
	Human h1;
	Human h2 = h1;
	Human h3(h1);

	h1.description();
	h2.description();
	h3.description();

	cout << "修改h1地址之后" << endl;

	char addrrr[ADDR_LEN] = "Ammerica";
	h1.setAddr(addrrr);

	h1.description();
	h2.description();
	h3.description();

	system("pause");
	return 0;
}

在这里插入图片描述

定义拷贝函数的情况下,给指针变量单独开辟新的空间,使得新的对象和旧的对象在调用该指针成员函数的时候,不再指向同一块内存
#include <iostream>
#include<string.h>
#include<graphics.h>

using namespace std;
#define ADDR_LEN 64  //宏

class Human {
   
   
public:
	Human();
	Human(int salary);
	void description();
	void setAddr(char *addr);
    /////////////////////////////////////////////
	Human(const Human &other);  //自定义拷贝函数,深拷贝
	/////////////////////////////////////////////
private:
	int age = 18;
	int salary;
	char *addr;
};

/////////////////////////////////////////////
Human::Human(const Human& other)
{
   
   
	age = other.age;
	salary = other.salary;

	//addr是指针,要为其分配一个独立的内存,来存放新的对象的addr,使得新的对象和旧的对象在调用该指针成员函数的时候,不再指向同一块内存
	this->addr = new char[ADDR_LEN];
	strcpy_s(this->addr, ADDR_LEN, other.addr);
}
/////////////////////////////////////////////

void Human::description()
{
   
   
	cout << "addr = " << addr << endl;
}

Human::Human(int salary)
{
   
   
	salary = 20000;
	this->addr = new char[ADDR_LEN];  //指针需要进行初始化分配空间
	strcpy_s(this->addr, ADDR_LEN, "china");
}

Human::Human()
{
   
   
	salary = 20000;
	this->addr = new char[ADDR_LEN];  //指针需要进行初始化分配空间
	strcpy_s(this->addr, ADDR_LEN, "china");
}

void Human::setAddr(char* addr)
{
   
   
	if (!addr)
	{
   
   
		return;
	}
	strcpy_s(this->addr, ADDR_LEN, addr);
}

int main(void)
{
   
   
	Human h1;
	Human h2 = h1;
	Human h3(h1);

	h1.description();
	h2.description();
	h3.description();

	cout << "修改h1地址之后" << endl;

	char addrrr[ADDR_LEN] = "Ammerica";
	h1.setAddr(addrrr);

	h1.description();
	h2.description();
	h3.description();

	system("pause");
	return 0;
}

在这里插入图片描述

(b) 什么时候拷贝构造函数会被调用

在这里插入图片描述

情形1:函数调用时,函数的实参是对象,形参不是引用类型,会调用拷贝构造函数导致额外的开销,这种情况推荐是使用引用,将不会调用拷贝构造函数,不会创新新的空间导致额外的开销

在这里插入图片描述
如果函数声明改成了 void showMsg(Human &man)则不对调用自定义构造函数,而是调用自动生成的构造函数

使用引用相当于传递了指针,可以在这个函数里面修改外面的参数。这样很危险,比如,下列函数的本意是打印信息,而不是修改成员函数,这样将导致打印的成员函数的值出现错误

void showMsg(Human &man)
{
   
   
  cout < man.getName() << endl;
  man.setAddr("Janpa“);
}

添加const修饰,使得在函数内不能修改成员函数
也就是void showMsg(const Human &man)
由于const修饰了man。man.getName()将会报错,因为man.getName()不是const类型的函数
所以 在定义的时候需要将string getName(); 改成string getName() const; 则表示不能在函数里面修改成员变量

#include <iostream>
#include<string.h>
#include<graphics.h>

using namespace std;
#define ADDR_LEN 64  //宏

class Human {
   
   
public:
	Human();
	Human(int salary);
	void description();
	void setAddr(char *addr);
	Human(const Human &other);  //自定义拷贝函数,深拷贝
private:
	int age = 18;
	int salary;
	char *addr;
};


Human::Human(const Human& other)
{
   
   
	age = other.age;
	salary = other.salary;

	//addr是指针,要为其分配一个独立的内存,来存放新的对象的addr,使得新的对象和旧的对象在调用该指针成员函数的时候,不再指向同一块内存
	this->addr = new char[ADDR_LEN];
	strcpy_s(this->addr, ADDR_LEN, other.addr);

	cout << "调用了拷贝构造函数" << endl;
}


void Human::description()
{
   
   
	cout << "addr = " << addr << endl;
	cout << "salary = " << salary << ",age = " << age << endl;
}

Human::Human(int salary)
{
   
   
	salary = 20000;
	this->addr = new char[ADDR_LEN];  //指针需要进行初始化分配空间
	strcpy_s(this->addr, ADDR_LEN, "china");
}

Human::Human()
{
   
   
	salary = 20000;
	this->addr = new char[ADDR_LEN];  //指针需要进行初始化分配空间
	strcpy_s(this->addr, ADDR_LEN, "china");
}

void Human::setAddr(char* addr)
{
   
   
	if (!addr)
	{
   
   
		return;
	}
	strcpy_s(this->addr, ADDR_LEN, addr);
}

void showMsg(Human man)
{
   
   
	cout << "showMsg :  " << endl;
	man.description();
}

int main(void)
{
   
   
	Human h1;
	//Human h2 = h1;

	showMsg(h1);
	//Human h3(h1);

	//h1.description();
	//h2.description();
	//h3.description();

	//cout << "修改h1地址之后" << endl;

	char addrrr[ADDR_LEN] = "Ammerica";
	h1.setAddr(addrrr);

	//h1.description();
	//h2.description();
	//h3.description();

	system("pause");
	return 0;
}

在这里插入图片描述

情形2:函数的返回值是类的情况,且这个类不是引用会调用构造函数才能构造一个对象。如果返回值是类引用,则是指针,直接使用原始数据,而不是先开辟空间来存放数据之后再使用,这样就不需要构建一个对象

下面这种情况是返回值是类的情况,会调用自定义的构造函数

Human getBetterMan(Human& man1, Human& man2)
{
   
   
	if (man1.getSalary() > man2.getSalary())
	{
   
   
		return man1;
	}
	else
	{
   
   
		return man2;
	}
}

int main(void)
{
   
   
	Human h1(35000);
	Human h2(25000);
	
	getBetterMan(h1, h2);  //这里这么写,会返回一个临时对象,但是没有东西接收,有一个对象需要调用构造函数才能构造一个对象,此处调用的是拷贝构造函数


	system("pause");
	return 0;
}

下面这种情况是返回值是类引用,不会调用自定义的构造函数

const Human& getBetterMan(const Human& man1, const Human& man2)
{
   
   
	if (man1.getSalary() > man2.getSalary())
	{
   
   
		return man1;
	}
	else
	{
   
   
		return man2;
	}
}
  • const常见错误

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

情形3:初始化成员列表

在这里插入图片描述

2 析构函数

主要是动态指针内存的清理。
析构函数不能带参数。只有一种形式
在这里插入图片描述

class Human {
   
   
public:
	Human();
	Human(int salary);
	void description() const;
	void setAddr(char* addr);
	void showMsg(const Human& man);
	int getSalary() const;
	Human(const Human& other);  //自定义拷贝函数,深拷贝
	
	//析构函数
	~Human();  //析构函数不能带参数
private:
	int age = 18;
	int salary;
	char* addr;
};


Human::~Human()
{
   
   
	cout << "调用析构函数 : " << this << endl;
	delete addr;
}

3 静态成员

3.1 静态成员

统计总共创建了多少个类对象,什么时候使用静态成员,当需要使用的数据是涉及到所有对象的时候,所有对象都要使用的时候,就需要定义静态成员函数
在这里插入图片描述

方法1:定义全局变量
#pragma once

#include<string.h>
#include <iostream>
using namespace std;
#define ADDR_LEN 64  //宏
extern int HumanCount; // 全局变量别人都可以修改,可能重名且不安全,开发的时候全局变量越少越好

class Human {
   
   
public:
	Human();
	Human(int salary);
	void description() const;
	void setAddr(char* addr);
	void showMsg(const Human& man);
	int getSalary() const;
	Human(const Human& other);  //自定义拷贝函数,深拷贝

	//析构函数
	~Human();  //析构函数不能带参数
private:
	int age = 18;
	int salary;
	char* addr;

	
	//int HumanCount;  //直接在这里使用这样是不行的,每个对象有自己的HumanCount,没办法做对象的计数
	static int HumanCount;  //静态成员变量的内存和类本身并不是一块内存
};

#pragma once

#include<string.h>
#include <iostream>
using namespace std;
#define ADDR_LEN 64  //宏
extern int HumanCount; // 全局变量别人都可以修改,可能重名且不安全,开发的时候全局变量越少越好

class Human {
   
   
public:
	Human();
	Human(int salary);
	void description() const;
	void setAddr(char* addr);
	void showMsg(const Human& man);
	int getSalary() const;
	Human(const Human& other);  //自定义拷贝函数,深拷贝

	//析构函数
	~Human();  //析构函数不能带参数
private:
	int age = 18;
	int salary;
	char* addr;

	
	//int HumanCount;  //直接在这里使用这样是不行的,每个对象有自己的HumanCount,没办法做对象的计数
	static int HumanCount;  //静态成员变量的内存和类本身并不是一块内存
};
#include<string.h>
#include <iostream>
using namespace std;
#define ADDR_LEN 64  //宏
#include "Human.h"

int HumanCount = 0;

Human::~Human()
{
   
   
	cout << "调用析构函数 : " << this << endl;
	delete addr;
}

int Human::getSalary() const
{
   
   
	return salary;
}

Human::Human(const Human& other)
{
   
   
	age = other.age;
	salary = other.salary;

	//addr是指针,要为其分配一个独立的内存,来存放新的对象的addr,使得新的对象和旧的对象在调用该指针成员函数的时候,不再指向同一块内存
	this->addr = new char[ADDR_LEN];
	strcpy_s(this->addr, ADDR_LEN, other.addr);

	cout << "调用了自定义拷贝构造函数" << endl;
}


void Human::description() const
{
   
   
	cout << "addr = " << addr << endl;
	cout << "salary = " << salary << ",age = " << age << endl;
}


Human::Human()
{
   
   
	salary = 20000;
	this->addr = new char[ADDR_LEN];  //指针需要进行初始化分配空间
	strcpy_s(this->addr, ADDR_LEN, "china");
	HumanCount++;
}

Human::Human(int salary)
{
   
   
	this->salary = salary;
	HumanCount++;
}

void Human::setAddr(char* addr)
{
   
   
	if (!addr)
	{
   
   
		return;
	}
	strcpy_s(this->addr, ADDR_LEN, addr);
}
方法2:定义静态成员函数

静态成员变量的内存和类本身并不是一块内存
在类里面定义静态成员
在这里插入图片描述

静态成员的初始化,然后就像使用全局变量一样使用他就可以
在这里插入图片描述
在这里插入图片描述

如果使用const标识的静态成员函数
静态成员函数在定义的时候就可以直接初始化,但是非const的静态成员函数定义的时候不能初始化

const static int humanCount = 0;  

或者去.cpp咯i面
const int Human::humanCount = 0;  
#pragma once

#include<string.h>
#include <iostream>
using namespace std;
#define ADDR_LEN 64  //宏

class Human {
   
   
public:
	Human();
	Human(int salary);
	void description() const;
	void setAddr(char* addr);
	void showMsg(const Human& man);
	int getSalary() const;
	Human(const Human& other);  //自定义拷贝函数,深拷贝

	//析构函数
	~Human();  //析构函数不能带参数
	int getHumanCount();

private:
	int age = 18;
	int salary;
	char* addr;

	
	//int HumanCount;  //直接在这里使用这样是不行的,每个对象有自己的HumanCount,没办法做对象的计数
	static int HumanCount;  //静态成员变量的内存和类本身并不是一块内存
};
#include<string.h>
#include <iostream>
using namespace std;
#define ADDR_LEN 64  //宏
#include "Human.h"


//对类的静态成员初始化
int Human::HumanCount = 0;
。。。。

int Human::getHumanCount()
{
   
   
	return HumanCount;
}

void showMsg(const Human &man)
{
   
   
	cout << "showMsg :  " << endl;
	man.description();
}

void test()
{
   
   
	Human h1;

	{
   
   
		Human h2;// h2先死,然后h1死
	}
	cout << "test() end." << endl;
}


int main(void)
{
   
   
	Human f1, f2, f3, f4;
	Human F4[4] = {
   
    f1, f2, f3, f4};

	test();
	cout << "总人数 : " << f1.getHumanCount() << endl;
	

	system("pause");
	return 0;
}

4 静态成员函数

使用常规的成员函数调用静态成员变量存在一个问题,见void showMsg()函数


void showMsg()
{
   
   
	//这里为了调用getHumanCount来调用静态成员变量,而定义了一个新的对象,导致最后的计数是3,但是实际在test里面只实际创建了两个对象,整个函数里创建这个对象是对于的
	Human tem1;
	cout << "总人数 : " << tem1.getHumanCount() << endl;
}

void test()
{
   
   
	Human h1;
    Human h2;
}


int main(void)
{
   
   
	test();
	showMsg();

	

	system("pause");
	return 0;
}

#####################################
定义静态成员函数

#pragma once

#include<string.h>
#include <iostream>
using namespace std;
#define ADDR_LEN 64  //宏

class Human {
   
   
public:
	static int getHumanCount();

private:
	//int HumanCount;  //直接在这里使用这样是不行的,每个对象有自己的HumanCount,没办法做对象的计数
	static int HumanCount;  //静态成员变量的内存和类本身并不是一块内存
};

//对类的静态成员初始化
int Human::HumanCount = 0;

int Human::getHumanCount()
{
   
   
	return HumanCount;
}
void showMsg()
{
   
   
	//static定义的变量或者函数,不属于某一个具体的对象,而是类的东西,可以直接通过类访问
	cout << "总人数 : " << Human::getHumanCount() << endl;
}

void test()
{
   
   
	Human h1;
    Human h2;
    //对象也可以直接访问静态成员函数
    cout << "总人数 : " << h1.getHumanCount() << endl;
}

int main(void)
{
   
   
	test();
	showMsg()system("pause");
	return 0;
}

在这里插入图片描述
静态成员函数如果直接访问类的成员变量,那就不知道访问的是哪一个具体对象的成员变量,是不行的

  • 静态的成员函数,不能调用非静态的成员函数(static定义的一般是类的东西,共用的东西,而用户可以创建很多类对象,每个类对象可以定义自己的变量,所以用公共的东西去访问个人的东西是不合理的)
    在这里插入图片描述
  • 静态成员函数不能访问非静态成员函数(static定义的一般是类的东西,共用的东西,而用户可以创建很多类对象,每个类对象可以定义自己的变量,所以用公共的东西去访问个人的东西是不合理的)
    -、

5 组合和聚合

组合和聚合最大的区别在于,
前者是类之间是高度关联的,同生同灭,一起创建,一起销毁
后者类之间互不关联

5.1 组合,一个类里面调用另一个类,非指针的形式

学习要点

  • 一个类在另一个类里面的定义方式
  • 一个类里面对另一个类的初始化方式

在这里插入图片描述
在这里插入图片描述

主函数

#include<string>
#include<stdio.h>
#include<iostream>
#include "Computer.h"
using namespace std;
void test()
{
   
   
   Computer myComputer("intel","i9",1000,8);
}
int main()
{
   
   
	test();return 0;
}

Computer.h, 一个类在另一个类里面的定义方式

#pragma once
#include<string>
#include<stdio.h>
#include<iostream>
#include "Cpu.h"
using namespace std;
//class Cpu;
class Computer
{
   
   
public:
	Computer(const char* cpuBrand, const char* cpuVersion, int hardDisk, int memory);
	~Computer();
private:
	//声明方式1
	Cpu cpu;  //如果声明用#include "Cpu.h",这里定义了一个对象,那就会需要自动调用它的构造函数,因此必须要包含头文件
	//声明方式2,下面是定义了一个指针,还没有创建对象,指针可以指向任何东西,所以不需要包含头文件
	//Cpu *cpu;  //要用class Cpu声明;
	int hardDisk;  //硬盘,单位 G
	int memory;  // 内存
};

Computer.cpp, 一个类里面对另一个类的初始化方式

#include "Computer.h"
Computer::Computer(const char* cpuBrand, const char* cpuVersion,
	int hardDisk, int memory):cpu(cpuBrand, cpuVersion)  //对成员函数对象初始化的方式1
{
   
   
	//this->cpu = Cpu(cpuBrand, cpuVersion); 对成员函数对象初始化的方式2
	this->hardDisk = hardDisk;
	this->memory = memory;
}
Computer::~Computer()
{
   
   

}

Cpu.h

#pragma once
using namespace std;
#include<string>
class Cpu
{
   
   
public:
	Cpu(const char *brand= "intel", const char* version = "i5");
	~Cpu();
private:
	string brand; //品牌
	string version; //型号
};

Cpu.cpp

#include "Cpu.h"
using namespace std;
#include<string>
#include<iostream>
Cpu::Cpu(const char* brand, const char* version)
{
   
   
	this->brand = brand;
	this->version = version;
	//打印当前函数名
	cout << __FUNCTION__ << endl;
}
Cpu::~Cpu()
{
   
   
	cout << __FUNCTION__ << endl;
}

5.1.2 组合,一个类里面调用另一个类,指针的形式

Computer.h

#pragma once
#include<string>
#include<stdio.h>
#include<iostream>

using namespace std;

class Cpu;

class Computer
{
   
   
public:
	Computer(const char* cpuBrand, const char* cpuVersion, int hardDisk, int memory);
	~Computer();

private:
	//声明方式1
	//Cpu cpu;  //如果声明用#include "Cpu.h",这里定义了一个对象,那就会需要自动调用它的构造函数,因此必须要包含头文件
	//声明方式2,下面是定义了一个指针,还没有创建对象,指针可以指向任何东西,所以不需要包含头文件
	Cpu *cpu;  //要用class Cpu声明;

	int hardDisk;  //硬盘,单位 G
	int memory;  // 内存
};

Computer.cpp

#include "Computer.h"

Computer::Computer(const char* cpuBrand, const char* cpuVersion,
	int hardDisk, int memory)
{
   
   
	this->cpu = new Cpu(cpuBrand, cpuVersion); //对成员函数对象初始化的方式2
	this->hardDisk = hardDisk;
	this->memory = memory;

}

Computer::~Computer()
{
   
   
	delete cpu;
}

5.2 聚合

下面案例对VoiceBox* box;的调用就是所谓聚合关系,Computer的销毁不会引起VoiceBox的销毁

class Computer
{
   
   
public:
	Computer(const char* cpuBrand, const char* cpuVersion, int hardDisk, int memory);
	~Computer();
	void addVoiceBox(VoiceBox* box);
private:
	//声明方式1
	//Cpu cpu;  //如果声明用#include "Cpu.h",这里定义了一个对象,那就会需要自动调用它的构造函数,因此必须要包含头文件
	//声明方式2,下面是定义了一个指针,还没有创建对象,指针可以指向任何东西,所以不需要包含头文件
	Cpu *cpu;  //要用class Cpu声明;
	VoiceBox* box;
	int hardDisk;  //硬盘,单位 G
	int memory;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值