从C到C++超基础教程(二)

12、构造函数和析构函数

自动调用构造函数

struct Date {
	int d, m, y;
	Date(int dd,int mm,int yy) {
		d = dd; m = mm; y = yy;
	}
	void print() {
		cout << y << "-" << m << "-" << d << endl;
	}
};
int main()
{
	Date day(19,3,2021);//自动调用Date(***)
	return 0;
}

但是如果写

Date day;//错误
因为没有默认构造参数,要加上Date(){}才可以,或者Date(int dd = 1,int mm = 3,int yy = 2020)加上默认参数

构造函数有默认值的形式

struct Date {
	int d, m, y;
	Date(int dd = 1,int mm = 3,int yy = 2020) {
		d = dd; m = mm; y = yy;
	}
	void print() {
		cout << y << "-" << m << "-" << d << endl;
	}
};
int main()
{
	Date day;
	day.print();//2020-3-1
	return 0;
}

定义一个学生类,名字是char指针类型,初始化可以把n赋值给name吗?

struct student {
	char *name;
	int age;
	student(char* n = "no_name", int a = 15) {
		name = n;/**************?/
		age = a;
	}
};

不能,指针变量不能给指针变量赋值

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<cstring>
using namespace std;

struct student {
	char *name; 
	int age;
	student(const char *n= "no_name", int a = 15) {
		int len = strlen(n);
		name = new char[len+1];//还有一个接受字符,多分配一个
		strcpy(name, n);
		age = a;
	}
};
int main()
{
	student stu1;
	student stu2("wang");
	student stu3("zhang",23);
	cout <<"stu1 "<< stu1.age << "\t" << stu1.name << endl;
	cout <<"stu2 " << stu2.age << "\t" << stu2.name << endl;
	cout <<"stu3 " << stu3.age << "\t" << stu3.name << endl;
	
	return 0;
}

析构函数

假如调用了f()函数,结束的时候stu1,stu2都会自动销毁
最后定义的变量先销毁

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<cstring>
using namespace std;

struct student {
	char *name;
	int age;
	student(const char *n= "no_name", int a = 15) {
		int len = strlen(n);
		name = new char[len+1];
		strcpy(name, n);
		age = a;
	}
	virtual ~student() {
		cout << "destructor!" <<name<< endl;
		delete[] name;//防止内存泄漏,其他程序无法使用这块内存
	}
};
void f()
{
	student stu1;
	student stu2("wang");
	student stu3("zhang",23);
	cout <<"stu1 "<< stu1.age << "\t" << stu1.name << endl;
	cout <<"stu2 " << stu2.age << "\t" << stu2.name << endl;
	cout <<"stu3 " << stu3.age << "\t" << stu3.name << endl;
}
int main() {
	f();
	//输出结果:
	/*
	* stu1 15 no_name
	* stu2 15 wang
	* stu3 23 zhang
	* destructor! zhang
	* destructor! wang
	* destructor! no_name
	*/
	return 0;
}

13、访问控制与接口

现在我们把struct改为class,发现不能访问该类,为什么呢?
加个public就可以了

class student {
public:
	char *name;
	int age;
	student(const char *n= "no_name", int a = 15) {
		int len = strlen(n);
		name = new char[len+1];
		strcpy(name, n);
		age = a;
	}
	virtual ~student() {
		cout << "destructor!" <<name<< endl;
	}
};

进一步把name和age改为私有,在public中通过函数就可以访问私有的name和age

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<cstring>
using namespace std;

class student {
private:
	char* name;
	int age;
public:
	student(const char *n= "no_name", int a = 15) {
		int len = strlen(n);
		name = new char[len+1];
		strcpy(name, n);
		age = a;
	}
	char* get_name() {
		return name;
	}
	int get_age() {
		return age;
	}
	virtual ~student() {
		cout << "destructor!" <<name<< endl;
	}
};
int main()
{
	student stu1;
	student stu2("wang",23);
	cout <<"stu1 " << stu1.get_name() << "\t" << stu1.get_age() << endl;
	cout <<"stu2 " << stu2.get_name() << "\t" << stu2.get_age() << endl;
	return 0;
}

如果想修改私有的name和age呢?加一个set_name和set_age
set_name需要重新分配空间

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<cstring>
using namespace std;

class student {
private:
	char* name;
	int age;
public:
	student(const char *n= "no_name", int a = 15) {
		int len = strlen(n);
		name = new char[len+1];
		strcpy(name, n);
		age = a;
	}
	char* get_name() {return name;}
	int get_age() {return age;}
	void set_name(const char* n = "new_name") 
	{
	/********************注意要释放原来的空间*************************/
		delete[] name;
		int len = strlen(n);
		name = new char[len+1];
		strcpy(name, n);
	}
	void set_age(int a) { age = a; }
	virtual ~student() {cout << "destructor!" <<name<< endl;}
};
int main()
{
	student stu1;
	stu1.set_name("xiao");
	stu1.set_age(18);
	cout <<"stu1:  " << stu1.get_name() << "\t" << stu1.get_age() << endl;
	return 0;
}

接口:public的公开成员(一般是成员函数)称为这个类的对外接口,外部函数只能通过这些类来访问private等非public的包含内部细节,从而可以封装保护对象

14、拷贝构造函数、赋值运算符

拷贝构造函数:定义一个类对象同时用另一个对象初始化
赋值运算符:一个对象赋值给另一个对象
下列的这两句都是系统自带的,但是可能出问题。

	student m(s);
	s = k;//赋值运算符

下面的代码调用了几次构造函数?

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<cstring>
using namespace std;

class student {
public:
	char* name;
	int age;
	student(const char *n= "no_name", int a = 0) {
		name = new char[100];//name指向这一块动态内存
		strcpy(name, n);
		age = a;
		cout << "申请了100个char动态空间" << endl;
	}
	virtual ~student() 
	{
		cout << "析构函数  " <<name<< endl;
		//delete[] name;//释放动态内存
	}
};
int main()
{
	student s;
	student m(s);//用s初始化m,拷贝构造函数
	cout << "m: " << m.name << " " << m.age << endl;
	return 0;
}/*输出:
申请了100个char动态空间
m: no_name 0
析构函数
*/

只在定义s的时候调用了构造函数;但是student m(s)的时候调用的拷贝构造函数是编译器帮我们生成的,没有输出构造函数。
最后析构函数要delete[] m.name,硬拷贝,m和s是完全一样的。
再来看赋值运算符的用法

	student s;
	student k("John", 56);
	cout << "k: " << k.name << " " << k.age << endl;
	s = k;
	cout << "s: " << s.name << " " << s.age << endl;

自己运行一下上面代码,就知道会出很多问题,下面来自己定义拷贝构造函数

	student(const student& s) {//这里加const,防止s被改变,拷贝构造函数的参数类型必须是引用
/*
       如果拷贝构造函数中的参数不是一个引用,即形如student(const student s),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。因此拷贝构造函数的参数必须是一个引用。
*/
		name = new char[100];
		strcpy(name, s.name);
		age = s.age;
		cout << "拷贝构造函数" << endl;
	}
	//赋值,返回本对象
	student& operator =(const student& s) {
		name = new char[100];
		strcpy(name, s.name);
		age = s.age;
		cout << "赋值运算符" << endl;
		return *this;
	}

整体来运行一下,看看结果是什么

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<cstring>
using namespace std;

class student {
public:
	char* name;
	int age;
	student(const char *n= "no_name", int a = 0) {
		name = new char[100];//name指向这一块动态内存
		strcpy(name, n);
		age = a;
		cout << "构造函数" << endl;
	}
	student(const student& s) {
		name = new char[100];
		strcpy(name, s.name);
		age = s.age;
		cout << "拷贝构造函数" << endl;
	}
	student& operator =(const student& s) {
		name = new char[100];
		strcpy(name, s.name);
		age = s.age;
		cout << "赋值运算符" << endl;
		return *this;
	}
	virtual ~student() 
	{
		delete[] name;
		cout << "析构函数  " << endl;
		
	}
};
int main()
{
	student s;//调用一次构造
	student k("John", 56);//调用一次构造
	cout << "k: " << k.name << " " << k.age << endl;
	s = k;//调用一次赋值
	cout << "s: " << s.name << " " << s.age << endl;

	student m(s);//d调用一次拷贝
	cout << "m: " << m.name << " " << m.age << endl;
	return 0;
	//最后输出三个析构s,k,m
}

15、类体外定义成员函数

格式:调用Date类的print()函数

void Date::print() {
}

16、类模板

和函数模板类似,我们可以把一个类变成模板类
下图代码的功能是定义一个Array类,重载[ ]运算符

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<cstring>
using namespace std;

class Array{
	int size;
	double* data;
public:
	Array(int s) {
		size = s;
		data = new double[s];
	}
	//重载一个下标运算符
	double& operator[](int i) {
		if (i < 0 || i >= size) {
			cerr << endl << "Out of bounds" << endl;
			exit(EXIT_FAILURE);
		}
		else return data[i];
	}
	~Array() {
		delete[] data;
	}
};
int main()
{
	Array t(5);
	t[0] = 1;
	t[4] = t[0] + 10;
	cout << t[4] << endl;
	
	return 0;
}

这个数组有什么问题?只能存放double类的data。
所以引入模板类,把double都换成T,主函数中告诉他,我的数组成员是int,编译器就把T换成int

Array<int> t(5);

完整代码

#define _CRT_SECURE_NO_WARNINGS//windows系统下防止出错误
#include <iostream>
#include<string>
using namespace std;
template<class T>
class Array{
	int size;
	T* data;
public:
	Array(int s=0) {
		size = s;
		data = new T[s];
	}
	//重载一个下标运算符
	T& operator[](int i) {
		if (i < 0 || i >= size) {
			cerr << endl << "Out of bounds" << endl;
			exit(EXIT_FAILURE);
		}
		else return data[i];
	}
	~Array() {
		delete[] data;
	}
};

int main()
{
	int i;
	Array<int> t(5);
	t[0] = 1;
	t[4] = t[0] + 10;
	for (i = 0; i < 5; i++) {
		cout << t[i] << " ";
	}
	cout << endl;
	//模板string类
	Array<string> d(3);
	d[0] = "hello";
	d[1] = "world";
	for (i = 0; i < 3; i++) {
		cout << d[i] << endl;
	}
	return 0;
}

17、别名typedef

a就是int类型

typedef int a;

18、string-vector

string

string是C++标准库中的类型

#include<string>

简单的string输出

#include <iostream>
#include<string>
using namespace std;
int main()
{
	string s1;//默认构造函数,没有参数或参数有默认值
	s1 = "fuzhi";//赋值运算符
	string s2("hello");//普通构造函数
	string s3(s1);//拷贝构造函数 string s3 = s1;

	cout << "s1 is:  " << s1 << endl;
	cout << "s2 is:  " << s2 << endl;
	cout << "s3 is:  " << s3 << endl;
	return 0;
}//输出 fuzhi,fuzhi,hello

拷贝构造函数的其他形式

string s4("this is a string", 10);	//表示从this is a string中取前10个字符构成s4
string s5(s4,6,4); 						//s5是从s4中下标为6的字符开始,取4个构成s5
string s6(15, '*');							//s6是15个*构成的字符串
string s7(s4.begin(), s4.end()-5 );//s7是从s4.begin() 到 s4.end()-5
string s8 = s4+ "add" + s2;			//字符串连接

遍历string,方法有1、下标;2、迭代器

#include <iostream>
#include<string>
using namespace std;
int main()
{
	string s="hello";
	string w = "world";
	s = s + w;

	for (int i = 0; i < s.size(); i++) {
		cout << i<<"  "<< s[i] <<endl;
	}
	return 0;
}

另一种遍历方法,string中的常量叠加器iterator

int main()
{
	string s="hello";
	string::const_iterator i;
	for (i = s.begin(); i < s.end(); i++) {
		cout <<"  "<< *i <<endl;
	}
	return 0;
}

非常量叠代器,可以修改字符

string s="hello";
	string::iterator i;
	for (i = s.begin(); i < s.end(); i++) {
		*i = 'A';
		cout <<"  "<< *i <<endl;
	}

vector

首先头文件引入

#include<vector>

具体怎么用这个类模板呢?

#include <iostream>
#include<vector>
using namespace std;
int main()
{
	//定义了一个数据为double型的s数组向量
	vector<double> score_stu;
	int stu = 5;
	//resize重新改编score的大小
	score_stu.resize(stu);
	//把5个学生的成绩输入
	for (vector<double>::size_type i = 0; i < stu; i++) {
		cout << "Enter score for student: " << i + 1 << " : " << endl;
		cin >> score_stu[i];
	}
	cout << endl;
	//输出刚刚输入的
	for (vector<double>::iterator j = score_stu.begin(); j < score_stu.end(); j++) {
		cout<< *j<<" ";
	}
	return 0;
}

输出也可以这样

for (vector<double>::size_type j = 0; j < stu; j++) {
		cout<< score_stu[j]<<" ";
	}

19、派生类

子类继承父类

#include <iostream>
using namespace std;
class Employee
{
	string name;
public:
	Employee(string n);
	void print();
};
//Manager除了雇员具有的特点外,还有自己特有的功能
class Manager :public Employee {
	int level;
public:
	Manager(string n, int le = 1);
};

//在类外实现构造函数Employee(string n)
Employee::Employee(string n) :name(n) {//初始化成员列表
	//name = n ;
}

//在类外实现构造函数void print()
void Employee::print() {
	cout << name << endl;
}

//实现Manager中的构造函数
Manager::Manager(string n,int le): Employee(n),level(le){
}

int main()
{
	Manager m("zhang");
	Employee e("Li");
	m.print();
	e.print();
	return 0;
}

派生类的构造函数只能描述它自己的成员和其父类的初始式,不能初始化基类的成员
所以下面类外实现Manager的代码是错的

Manager::Manager(string n,int le): name(n),level(le){
}			//name是父类私有的

也可以重载Manager::print()函数

#include <iostream>
using namespace std;
class Employee
{
	string name;
public:
	Employee(string n) :name(n) {};
	void print() {
		cout << name << endl; 
	}
};

class Manager :public Employee {
	int level;
public:
	Manager(string n, int le = 1) : Employee(n), level(le) {};
	void print();
};

void Manager::print() {
	cout << "Manager:  " <<"   ";
	Employee::print();
}

int main()
{
	Manager m("zhang");
	Employee e("Li");
	m.print();//变化输出:Manger:
	e.print();
	return 0;
}

定义一个指针也是可以的


int main()
{
	Manager m("zhang");
	Employee* e = &m;
	e->print();
	return 0;
}

20、虚函数和多态

如何定义一个Employee的数组

int main()
{
	int num=0;
	Employee* e[10];//有10个雇员的数组
	Employee m("zhang");
	Employee n("liu");
	e[num++] = &m;
	e[num++] = &n;
	cout << num << endl;
	return 0;
}

虚函数Virtual Functions

指向父类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;

int main()
{
	Employee* p;
	Manager m("zhang", 2);
	Employee e("Liu");
	
	p = &e;
	p->print();//liu
	p = &m;
	p->print();//*******输出zhang
	return 0;
}

p->print();输出”zhang“,没有 “Manager: " <<” ";
p是Employee的指针类型,输出了Employee的zhang,而不是Manager中的print: “Manager: " <<” ";
所以把Employee的print()变为虚函数,就能输出Manager: zhang

#include <iostream>
using namespace std;
class Employee
{
	string name;
public:
	Employee(string n) :name(n) {};
	virtual void print() {
		cout << name << endl; 
	}
};

class Manager :public Employee {
	int level;
public:
	Manager(string n, int le = 1) : Employee(n), level(le) {};
	void print();
};

void Manager::print() {
	cout << "Manager:  " <<"   ";
	Employee::print();
}

int main()
{
	Employee* p;
	Manager m("zhang", 2);
	Employee e("Liu");
	
	p = &e;
	p->print();//liu
	p = &m;
	p->print();//虽然p是Employee的指针类型,但是它指向了Manager,所以可以输出zhang
	return 0;
}

多态

#include <iostream>
using namespace std;
class Employee
{
	string name;
public:
	Employee(string n) :name(n) {};
	virtual void print() {
		cout << name << endl; 
	}
};

class Manager :public Employee {
	int level;
public:
	Manager(string n, int le = 1) : Employee(n), level(le) {};
	void print();
};

void Manager::print() {
	cout << "Manager:  " <<"   ";
	Employee::print();
}

int main()
{
	Employee* e[10];
	int num = 0;//雇员数目
	Employee* p;
	string name;
	int level;
	char c;
	cout << "M代表经理,其他代表雇员,请输入:" << endl;
	while (cin >> c) {
		if (c == 'M' || c == 'm') {//‘M/m’表示经理
			cout << "请输入经理的姓名和级别: ";
			cin >> name >> level;
			p = new Manager(name, level);//新建一个经理
			e[num++] = p;	//把经理放在数组中
		}
		else if (c == 'E' || c == 'e') {
			cout << "请输入雇员的姓名: ";
			cin >> name;//否则的话输入雇员
			p = new Employee(name);
			e[num++] = p;
		}
		else break;
		cout << "请继续输入" << endl;
	}
	for (int i = 0; i < num; i++) {
		e[i]->print();
	}
	return 0;
}

21、多重继承

当然,我们可以从一个类派生出多个不同的类

class Employee
{
public:
	virtual void print() {
		cout << "Employee:  " << "   ";
	}
};

class Manager :public Employee {
public:
	void print() {
		cout << "Manager:  " << "   ";
	}
};

class Secretary :public Employee {
public:
	void print() {
		cout << "Secretary:  " << "   ";
	}
};

也可以从多个不同的类中派生出一个类:多重派生(Multiple inheritance)

class one{public:
	//
};
class two{public:
	//
};

class MultipleInheritance :public one,public two {
public:
	//
};

22、纯虚函数和抽象类

纯虚函数(pure virtual function)和抽象类(abstract base class)
函数体=0的虚函数成为”纯虚函数“。包含纯虚函数的类称为”抽象类“

virtual const char* speak() = 0;

下面是一个抽象类的定义

#include <iostream>
#include <string>
using namespace std;
class Animal{
private:
	string m_name;
public:
	Animal(string name):m_name(name) {
	}
	string getName() {
		return m_name;
	}
	virtual const char* speak() = 0;
};

主函数如何定义一个Animal类呢?Animal a;可以吗?

int main()
{
	Animal a;//ERROR!!抽象类不能被实例化
	return 0;
}

Cow是派生出来的,也是抽象类,不能实例化

#include <iostream>
#include <string>
using namespace std;
class Animal{
private:
	string m_name;
public:
	Animal(string name):m_name(name) {
	}
	string getName() {
		return m_name;
	}
	virtual const char* speak() = 0;
};
class Cow :public Animal {
public:
	Cow(string name):Animal(name) {//Cow仍然是一个抽象类
	}
};

int main()
{
	Animal a("ani");//ERROR!!抽象类不能被实例化
	Cow c("niu");//ERROR!!抽象类不能被实例化
	return 0;
}

那应该如何定义,Cow类中重新定义纯虚函数即可

class Cow :public Animal {
public:
	Cow(string name):Animal(name) {//Cow仍然是一个抽象类
	}
	virtual const char* speak() = 0 {
		return "Moo";
	};
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值