C++学习记录2(面向对象语法——上)

C++学习2

​ 在上一节的学习中,主要还是在复习C语言的特性,包括最后的实战篇,也没用上面向对象的设计思想。但从这一节开始,正式开始学习属于C++的特性,并且学习面向对象的编程思想。

:本文是参考黑马程序员的C++视频教程后自己总结的,添加了一部分自己的东西,属于二次创作,全投转载也太那啥了,我干脆一个投转载一个投原创算了,谁让CSDN没有二次创作的选项呢(任性)。建议想要学习C++的小伙伴每人都自己写一篇总结,加深理解。

黑马程序员C++教程

主讲老师的账号主页

这是本人的学习记录,通篇又臭又长,不建议观看,有作业需求的自取即可

面向对象语法

new操作符

在C++中使用new关键字申请堆中内存

堆区申请的内存不会被系统自动释放,由程序员使用delete关键字手动释放

语法:new 数据类型

利用new创建的数据,会返回该数据对应类型的指针

示例:

#include <iostream>
#include <string>
using namespace std;


int main()
{
    /*在堆区申请一个整形*/
	int* p = new int(10);			//圆括号内的数据是为这块内存赋值用的
	cout << *p << endl;				//输出为10
	delete p;						//释放内存
    
    /*在堆区申请一个数组*/
    int* p1 = new int[10];			//方括号代表申请的是一个数组,方括号内数字代表数组元素个数
    for (int i = 0; i < 10; i++)	//为数组赋值
	{
		p1[i] = i;
	}
	for (int i = 0; i < 10; i++)	//打印数组,输出0到9
	{
		cout << p1[i] << endl;
	}
	delete[] p1;					//释放数组的时候要加一个方括号,告诉编译器释放的是一个数组

	system("pause");

	return 0;
}

总结:

  • 相比malloc,new可以在申请内存的同时给内存赋值,值放在圆括号内。这不光能为整形、浮点型赋值,也可以为string赋值(括号里加上双引号代表它是个字符串),甚至可以为自定义的结构体赋值(括号里需要加上大括号,如:student* p = new student({ 12, "张三" });
  • 在释放数组的时候,不要忘了在delete后面加上一对方括号。

引用

引用基本语法

作用:给变量起别名

语法:数据类型 &别名 = 原名;

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;


int main()
{
	int a = 10;			//定义a等于10

	int& b = a;			//b引用a

	cout << a << endl;	//a等于10
	cout << b << endl;	//b等于10
	
	b = 100;

	cout << a << endl;	//a等于100
	cout << b << endl;	//b等于100

	system("pause");

	return 0;
}
引用注意事项
  • 引用必须初始化
  • 引用在初始化后不可以改变

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;


int main()
{
	int a = 10;			//定义a等于10

	/*1、引用必须初始化*/
	//int& b;			//错误语法
	int& b = a;

	/*2、引用在初始化后不可改变*/
	int c = 20;
	//&b = c;			//错误语法
	b = c;				//语法没错,但这是赋值操作

	cout << a << endl;	//三者输出都为20
	cout << b << endl;
	cout << c << endl;


	system("pause");

	return 0;
}
引用做函数参数

作用:函数传参时,可以利用引用来使形参修改实参

优点:简化指针的操作,并节省形参中指针所占内存

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

void mySwap(int& a, int& b);

int main()
{
	int a = 10;		
	int b = 20;

	cout << "a = " << a << endl;	//打印交换前a、b的值
	cout << "b = " << b << endl;

	mySwap(a, b);					//交换a、b

	cout << "a = " << a << endl;	//打印交换后a、b的值
	cout << "b = " << b << endl;


	system("pause");

	return 0;
}

void mySwap(int& a, int& b)			//引用传参
{
	int temp = a;
	a = b;
	b = temp;
}
引用做函数返回值

作用:引用可以修改函数内部的静态局部变量

注意:不要返回局部变量的引用

用法:函数调用为左值

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

int& test1();
int& test2();

int main()
{
	/*引用局部变量的案例*/
	int& ref1 = test1();
	cout << "ref1 = " << ref1 << endl;		//第一次输出仍然是10,是因为栈的内存还未被改写,实际已是非法操作
	cout << "ref1 = " << ref1 << endl;		//二三两次输出的就不再是10
	cout << "ref1 = " << ref1 << endl;

	/*引用静态局部变量*/
	int& ref2 = test2();
	cout << "ref2 = " << ref2 << endl;		//静态局部变量的内存不会被自动释放,所以不去人为修改就不会变
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;

	/*函数调用作左值*/
	test2() = 1000;							//函数返回的是静态变量a的引用,所以这句语句会为a赋值
	cout << "ref2 = " << ref2 << endl;		//输出1000

	system("pause");

	return 0;
}

int& test1()
{
	int a = 10;
	return a;
}

int& test2()
{
	static int a = 10;
	return a;
}
引用的本质

本质:引用的本质在C++内部实现是一个指针常量

示例:

/*发现是引用,转化为 int* const ref = &a */
void func(int& ref)
{
    ref = 100;			//内部发现ref是引用,转化为: *ref = 100;
}
int main()
{
    int a = 10;
    
    /*自动转化为 int* const ref = &a; */
    int& ref = a;
    ref = 20;			//内部发现ref是引用,自动转化为: *ref = 20;
    
    
    func(a);
    return 0;
}

结论:推荐使用引用,因为语法方便。引用的本质是指针常量,但内部的实现编译器已经帮我们做了,不需要去深究

个人理解:

  • 引用和变量占用相同的内存,好似变量的影子
  • 引用的值就是变量的值,引用就是变量的本身,只是换了个名字

优点:

  • 相比于值传递,不但节省形参的内存,还可以通过形参修改实参
  • 相比于地址传递,可以节省形参中指针的内存
  • 可以使用引用修改函数内部的静态局部变量
常量引用

作用:常量引用主要用来修饰形参,防止误操作修改了实参

在函数形参列表中,可以加const修饰形参,防止形参修改实参

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

void showNum(const int& a);

int main()
{
	/*引用常量*/
	//int& ref = 10;		语法错误,引用必须引用一块合法的内存
	const int& ref = 10;	//合法操作,引用加const修饰可以引用常量

	/*常量引用作形参*/
	int a = 10;
	showNum(a);

	system("pause");

	return 0;
}

void showNum(const int& a)
{
	//a = 100;				语法错误,常量不可修改
	cout << a << endl;
}

函数提高

函数的默认参数

在C++中,函数的形参可以有默认值

语法:返回值类型 函数名(参数 = 默认值);

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

int test1(int a, int b = 20, int c = 30);	//函数声明含有默认参数

int main()
{
	cout << test1(10) <<endl;			//输出60:a=10;b=10;c=30
	cout << test1(10, 10) << endl;		//输出50:a=10;b=10;c=30
	cout << test1(10, 10, 10) << endl;	//输出30:a=10;b=10;c=10

	system("pause");

	return 0;
}

int test1(int a, int b, int c)			//函数声明含有默认参数,函数的定义就不能含有默认参数
{
	return a + b + c;
}

注意事项:

  • 一旦函数的某个形参使用了默认参数,那么这个形参右边的所有形参都必须含有默认参数。像int test1(int a, int b = 20, int c);这样的是不允许的。
  • 如果函数的声明含有默认参数,那么这个函数的定义中就不能含有默认参数。不然一旦函数声明和定义采用了不一样的默认参数,编译器就不知道该采用哪个默认参数。同样的,多次函数声明也只有一个声明能含有默认参数(在某个函数内部作函数声明可以赋予不同的默认参数,但不要这样做!!!)。
函数的占位参数

C++中函数的形参列表里可以有占位参数,用来做占位。调用函数时必须要填补该位置

语法:返回值类型 函数名(数据类型);

现阶段占位参数的意义不大,先做了解,后续会真正用到

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

void test1(int a, int);
void test2(int a, int = 100);

int main()
{	
	test1(10,100);		//输出a = 10。但后面的100在函数中用不到

	test2(10);			//占位参数还可以含有默认参数

	system("pause");

	return 0;
}

void test1(int a, int)
{
	cout << "a = " << a << endl;
}

void test2(int a, int)
{
	cout << "a = " << a << endl;
}
函数重载

作用:函数名可以相同,提高复用性

函数重载满足条件:

  • 同一个作用域下
  • 函数名相同
  • 函数参数类型不同或个数不同或顺序不同

注意:函数的返回值不同不可以作为函数重载的条件

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

struct Point
{
	int x;
	int y;
};

Point point;

void setPoint(int x, int y);
void setPoint(const Point* p);

int main()
{		
	setPoint(10, 20);
	cout << point.x << "," << point.y << endl;

    Point p;
	p.x = 20;
	p.y = 30;
	setPoint(&p);
	cout << point.x << "," << point.y << endl;

	system("pause");

	return 0;
}

void setPoint(int x, int y)
{
	point.x = x;
	point.y = y;
}

void setPoint(const Point* p)
{
	point.x = p->x;
	point.y = p->y;
}

总结:

函数重载使得函数名的复用性得到了提高,用户可以使用同一个函数名去调用不同的函数。像本例中,用户想要改变结构体point成员的值,可以使用两种方法,提高了自由度。但是,下面这两个函数是不可以被重载的:

void setPoint(int x);
void setPoint(int y);

注意:

函数的重载虽然为我们提供了一定的便利,但同时也带来了很多困扰,在设计程序时一定要极力避免

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

void test1(int a);
void test1(short a);

int main()
{	
	auto a = 10;
	int b = 10;
	short c = 10;
	char d = 10;

	test1(10);
	test1(a);
	test1(b);
	test1(c);
	test1(d);


	system("pause");

	return 0;
}

void test1(int a)
{
	cout << "我是int" << endl;
}

void test1(short a)
{
	cout << "我是short" << endl;
}

试问:如果看到了这样的代码,会不会想打人?

如果还觉得不够过瘾,那么这样呢?

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

void test1(int a, short b);
void test1(short a, int b);

int main()
{	
	unsigned int a = 10;
	int b = 10;
	short c = 10;
	char d = 10;

	test1(a, b);
	test1(c, d);
	test1(d, c);

	system("pause");

	return 0;
}

void test1(int a, short b)
{
	cout << "我是int" << endl;
}

void test1(short a, int b)
{
	cout << "我是short" << endl;
}
函数重载注意事项
  • 引用作为重载条件
  • 函数重载碰到函数默认参数

示例1:函数重载碰到引用

void test(int a);
void test(int& a);
void test(const int& a);

int main()
{
    int a = 10;
    test(a);
    
    return 0;
}

示例2:函数重载碰到默认参数

void test(int a);
void test(int a, int b = 10);

int main()
{
    int a = 10;
    test(a);
    
    return 0;
}

总结:

在使用函数重载时,一定要避免出现二义性。

类和对象

C++面向对象的三大特性为:封装、继承、多态

C++认为万事万物皆为对象,对象上有其属性和行为

例:

人可以作为对象,属性有姓名、身高、体重等,行为有走、跑、跳、吃饭唱歌等

车也可以作为对象

封装
封装的意义

封装是C++面向对象三大特性之一

封装的意义:

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制
封装的意义一:属性加行为

​ 在设计类的时候,属性和行为写在一起,表现事物

语法:class 类名{ 访问权限: 属性 / 行为 };

示例1:设计一个圆类,求圆的周长

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

#define	PI	3.14

class Circle				//圆类
{
public:						//公共权限
	/*属性*/
	int m_r;				//半径

	/*行为*/
	double calculateZC()	//求周长
	{
		return 2 * PI * m_r;
	}
};

int main()
{	
	/*通过圆类,创建具体的圆(对象)*/
	Circle c1;

	/*为其赋值*/
	c1.m_r = 10;

	/*求周长*/
	cout << "圈的周长为:" << c1.calculateZC() << endl;


	system("pause");

	return 0;
}

示例2:设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号

#include <iostream>
#include <string>
#include <ctime>
using namespace std;


class Student				//学生类
{
public:						//公共权限
	/*属性*/
	string m_name;			//姓名
	int m_num;				//学号

	/*行为*/
	void setName(string name)//设置姓名
	{
		m_name = name;
	}

	void setNum(int num)	//设置学号
	{
		m_num = num;
	}

	string getName()		//获取姓名
	{
		return m_name;
	}

	int getNum()			//获取学号
	{
		return m_num;
	}
};

int main()
{	
	/*创建一个学生对象*/
	Student s1;

	/*为它赋值*/
	s1.setName("张三");
	s1.setNum(18);

	/*获取它的值并打印*/
	cout << s1.getName() << endl;
	cout << s1.getNum() << endl;


	system("pause");

	return 0;
}
封装的意义二:权限

类在设计时,可以把属性和行为放在不同的权限下,加以控制

访问权限有三种:

  1. public 公共权限
  2. protected 保护权限
  3. private 私有权限

解释一下它们的含义:

  1. 公共权限:类内可以访问,类外也可以访问
  2. 保护权限:类内可以访问,类外不可以访问,但可以被子对象继承
  3. 私有权限:类内可以访问,类外不可以访问,且无法被子对象继承

注:类外访问也就是在类外的函数调用这样的语句:对象.属性 = xxx;

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;


class Person				//人类
{
public:						//公共权限
	string m_name;			//姓名

protected:					//保护权限
	string m_car;			//汽车

private:					//私有权限
	int m_password;			//密码

protected:					//方法同样可以设置权限
	void show1()			//保护权的限方法
	{
		cout << m_name << endl;
		cout << m_car << endl;			
		cout << m_password << endl;
	}

public:						//公共权限的方法	
	void init()
	{
		m_name = "张三";	   //这就是类内访问
		m_car = "拖拉机";
		m_password = 123456;
	}

	void show2()
	{
		show1();			//无论什么权限,在类内都可以被访问、调用
	}
};

int main()
{	
	/*创建一个人对象*/
	Person p1;
	p1.init();							//公共权限的方法,可以在类外被调用

	/*这就是类外访问,无论是读还是写*/
	p1.m_name = "李四";					//合法操作,因为它是公共权限
	//p1.m_car = "奔驰";				//语法错误,保护权限无法在类外访问
	//p1.m_password = 123;				//语法错误,私有权限无法在类外访问

	/*打印*/
	cout << p1.m_name << endl;
	//cout << p1.m_car << endl;			//这两句同样会报错
	//cout << p1.m_password << endl;

	/*调用两个不同权限的方法*/
	//p1.show1();						//编译器同样会报错
	p1.show2();


	system("pause");

	return 0;
}
struct和class的区别

在C++中,struct和class唯一的区别就是默认的访问权限不同:

  • struct默认全部为公共权限
  • class默认全部为私有权限
struct C1
{
	int m_a;			//默认为公共权限

	void init(int a)	//下面两个方法也默认是公共权限
	{
		m_a = a;
	}

	void show()
	{
		cout << m_a << endl;
	}
};

class C2
{
	int m_a;			//默认为私有权限

	void init(int a)	//下面两个方法也默认是私有权限
	{
		m_a = a;
	}

	void show()
	{
		cout << m_a << endl;
	}
};

总结:

在C++中,结构体同样可以包含方法,可以添加不同的权限,除了默认权限不同,其他都一样。

而在C语言中,结构体就没有这么强大了,所有的成员都只能是公共权限,且结构体中不能含有方法,只能通过函数指针来部分实现。

成员属性设为私有

优点:

  • 将所有成员属性设为私有,可以自己控制读写权限
  • 对于写权限,可以在方法中添加断言来检测输入数据有效性、合法性

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:
	void setName(string name)	//设置姓名 写
	{
		m_name = name;
	}
	string getName()			//获取姓名 读
	{
		return m_name;
	}

	int getAge()				//获取年龄 只读
	{
		m_age = 10;
		return m_age;
	}

	void setPassword(int password)//设置密码 只写
	{
		/*只允许设置5位数密码,设置错误自动还原成0*/
		if (password < 10000 || password>99999)
		{
			cout << "请设置5位数密码" << endl;
			m_password = 0;
			return;
		}
		m_password = password;
	}

private:
	string m_name;		//姓名 可读可写

	int m_age;			//年龄 只读

	int m_password;	//密码 只写
};


int main()
{	
	Person p1;

	/*姓名为可读可写*/
	p1.setName("张三");
	cout << p1.getName() << endl;

	/*年龄为只读*/
	cout << p1.getAge() << endl;

	/*密码为只写*/
	p1.setPassword(12345);


	system("pause");

	return 0;
}
练习案例1:设计立方体类

设计立方体类(Cube)

求出立方体的面积和体积

分别用全局函数和成员函数判断两个立方体是否相等

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Cube
{
public:
	/*设置长宽高*/
	void setSize(int l, int w, int h)
	{
		m_length = l;
		m_width = w;
		m_height = h;
	}
	/*获取长宽高*/
	int getLen()
	{
		return m_length;
	}
	int getWid()
	{
		return m_width;
	}
	int getHei()
	{
		return m_height;
	}
	/*计算周长*/
	int calculateS()
	{
		return 2 * (m_length * m_width + m_width * m_height + m_height * m_length);
	}
	/*计算面积*/
	int calculateV()
	{
		return m_length * m_width * m_height;
	}
	/*判断两个立方体是否相等*/
	bool judgeCube(Cube& cube)
	{
		int temp[2][3] = 
		{
			{m_length,      m_width,       m_height     },
			{cube.getLen(), cube.getWid(), cube.getHei()}
		};

		for (int i = 0; i < 3 - 1; i++)
		{
			for (int j = 0; j < 3 - 1 - i; j++)
			{
				if (temp[0][j] > temp[0][j + 1])
				{
					int buf = temp[0][j];
					temp[0][j] = temp[0][j + 1];
					temp[0][j + 1] = buf;
				}
				if (temp[1][j] > temp[1][j + 1])
				{
					int buf = temp[1][j];
					temp[1][j] = temp[1][j + 1];
					temp[1][j + 1] = buf;
				}
			}
		}

		if (temp[0][0] == temp[1][0] && temp[0][1] == temp[1][1] && temp[0][2] == temp[1][2])
		{
			return true;
		}

		return false;
	}
private:
	int m_length;	//长
	int m_width;	//宽
	int m_height;	//高
};

bool judgeCube(Cube& c1, Cube& c2);

int main()
{	
	/*创建一个立方体对象并赋值*/
	Cube cube1;
	cube1.setSize(10, 20, 30);
	/*打印这个立方体对象的面积和体积*/
	cout << "面积为:" << cube1.calculateS() << endl;
	cout << "体积为:" << cube1.calculateV() << endl;

	/*创建另一个立方体对象并赋值*/
	Cube cube2;
	cube2.setSize(20, 10, 30);

	/*调用成员函数判断两个立方体是否相等*/
	if (cube2.judgeCube(cube1) == true)
		cout << "一样" << endl;
	else
		cout << "不一样" << endl;

	/*创建第三个立方体并赋值*/
	Cube cube3;
	cube3.setSize(30, 20, 10);

	/*调用全局函数判断两个立方体是否相等*/
	if (judgeCube(cube3, cube1) == true)
		cout << "一样" << endl;
	else
		cout << "不一样" << endl;

	system("pause");

	return 0;
}

bool judgeCube(Cube& c1, Cube& c2)
{
	int temp[2][3] =
	{
		{c1.getLen(), c1.getWid(), c1.getHei()},
		{c2.getLen(), c2.getWid(), c2.getHei()}
	};

	for (int i = 0; i < 3 - 1; i++)				//先将两组数据从小到大排序
	{
		for (int j = 0; j < 3 - 1 - i; j++)
		{
			if (temp[0][j] > temp[0][j + 1])
			{
				int buf = temp[0][j];
				temp[0][j] = temp[0][j + 1];
				temp[0][j + 1] = buf;
			}
			if (temp[1][j] > temp[1][j + 1])
			{
				int buf = temp[1][j];
				temp[1][j] = temp[1][j + 1];
				temp[1][j + 1] = buf;
			}
		}
	}

	if (temp[0][0] == temp[1][0] && temp[0][1] == temp[1][1] && temp[0][2] == temp[1][2])
	{
		return true;							//小对小、中对中、大对大,三者全部相等则两个立方体全等
	}

	return false;
}

这个算法可能不是很好,但却是本人自己想出来的(如有雷同纯属巧合),就像是小时候做对了一道数学题,挺开心的。(当然各位如果有更好的算法也不妨让鄙人开开眼)

练习案例2:点和圆的关系

设计一个圆类(Circle),和一个点类(Point),计算点和圆的关系

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Point
{
public:
	/*设置xy*/
	void setPoint(int x, int y)
	{
		m_x = x;
		m_y = y;
	}
	/*获取xy*/
	int getX()
	{
		return m_x;
	}
	int getY()
	{
		return m_y;
	}
private:
	int m_x;
	int m_y;
};

class Circle
{
public:
	/*设置半径*/
	void setR(int r)
	{
		m_r = r;
	}
	/*获取半径*/
	int getR()
	{
		return m_r;
	}
	/*设置圆心*/
	void setCenter(int x, int y)
	{
		m_center.setPoint(x, y);
	}
	/*获取圆心*/
	int getX()
	{
		return m_center.getX();
	}
	int getY()
	{
		return m_center.getY();
	}
private:
	int m_r;
	Point m_center;
};

void isInCir(Circle& c, Point& p);

int main()
{	
	Circle c1;
	c1.setCenter(0, 0);
	c1.setR(10);

	Point p1;
	p1.setPoint(0, 11);

	isInCir(c1, p1);

	system("pause");

	return 0;
}

void isInCir(Circle& c, Point& p)
{
	/*计算两点间距离的平方*/
	int distance =
		(c.getX() - p.getX()) * (c.getX() - p.getX()) + 
		(c.getY() - p.getY()) * (c.getY() - p.getY());

	/*计算半径的平方*/
	int rDistance = c.getR() * c.getR();

	/*判断相对关系*/
	if (distance == rDistance)			//两者都先平方再判断大小,免去了开根号的运算。同时解决了两个浮点数不好判断相等的问题
	{
		cout << "点在圆上" << endl;
	}
	else if (distance > rDistance)
	{
		cout << "点在圆外" << endl;
	}
	else
	{
		cout << "点在圆内" << endl;
	}
}

对象的初始化和清理

C++中的对象在创建过程中其实还有初始化的过程,包括销毁时也有对应的处理。说得简单一些,就是对象在创建的过程中,会调用初始化的回调函数。之前我们并没有编写这类回调函数,是因为编译器会为我们提供默认的回调函数,默认的回调函数什么都不会做。

构造函数和析构函数

构造函数和析构函数就是上面说的回调函数,在对象创建、销毁过程中会被自动调用。

对象的初始化和清理是两个非常重要的工作:

  • 如果对象在初始化之前就被使用,很可能会发生错误
  • 如果对象申请了动态内存,在销毁对象前忘了释放内存就会发生内存泄漏

C++提供了构造函数和析构函数来解决上述问题,这两个函数会被编译器自动调用,完成对象的初始化和清理工作。

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构函数,编译器会提供默认的构造和析构函数。

编译器提供的构造和析构函数是空实现。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用
  • 析构函数:主要作用于对象销毁前系统自动调用,执行一些清理工作

构造函数语法:类名(){}

  1. 构造函数没有返回值,也不需要写返回值类型(void)
  2. 函数名称和类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在对象创建时会自动调用构造函数,且只会调用一次,无需手动调用。

析构函数语法:~类名(){}

  1. 析构函数没有返回值,也不需要写返回值类型
  2. 函数名为类名前面加上~符号
  3. 析构函数不能有参数,因此也不可以重载
  4. 程序在对象销毁时会自动调用析构函数,且只会调用一次,无需手动调用。

注:这两个函数同样有权限,最好都设为公共权限,以免带来不必要的麻烦

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:
    /*构造函数*/
	Person()
	{
		cout << "Person构造函数调用" << endl;
	}
    /*析构函数*/
	~Person()
	{
		cout << "Person析构函数调用" << endl;
	}
};

void test()
{
	Person p1;
}

int main()
{	
	test();

	system("pause");

	return 0;
}

解释:

  • 对象p1会在test函数中被创建,因此在创建的时候会自动调用一次构造函数,打印“Person构造函数调用”。

  • 由于p1对象是局部变量存放在栈中,因此test函数调用结束后会被系统自动释放,释放时会自动调用一次析构函数,打印“Person析构函数调用”。

构造函数的分类及调用

两种分类方式:

  1. 按参数分为:有参构造和无参构造
  2. 按类型分为:普通构造和拷贝构造

三种调用方式:

  1. 括号法
  2. 显示法
  3. 隐式转换法

示例:

1、括号法

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:
	/*构造函数*/
	Person()				//无参构造函数(默认构造函数)
	{
		cout << "Person无参构造函数调用" << endl;
	}
	Person(int a)			//有参构造函数
	{
		m_age = a;
		cout << "Person有参构造函数调用" << endl;
	}
	Person(const Person& p)	//拷贝构造函数
	{
		m_age = p.m_age;
		cout << "Person拷贝构造函数调用" << endl;
	}

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

	/*属性*/
	int m_age;
};

void test()
{
	/*1、括号法*/
	Person p1;			//默认构造函数调用
	Person p2(10);		//有参构造函数调用
	Person p3(p2);		//拷贝构造函数调用

	cout << "p2年龄为:" << p2.m_age << endl;
	cout << "p3年龄为:" << p3.m_age << endl;

	/*2、显示法*/

	/*3、隐式转换法*/

}

int main()
{	
	test();

	system("pause");

	return 0;
}

输出:

Person无参构造函数调用
Person有参构造函数调用
Person拷贝构造函数调用
p2年龄为:10
p3年龄为:10
Person析构函数调用
Person析构函数调用
Person析构函数调用

注意:

  • 在调用无参构造函数的时候,不要加括号,如:Person p1();。因为编译器会认为这句语句是一个函数的声明。
  • 注意拷贝构造函数的参数,一定是const修饰的,且是引用传递的

2、显示法

void test()
{
	/*1、括号法*/

	/*2、显示法*/
	Person p1;					//无参构造
	Person p2 = Person(10);		//有参构造
	Person p3 = Person(p2);		//拷贝构造

	/*3、隐式转换法*/

}

注意:

  • 本例中Person(10)为匿名对象,特点:在当前行执行完毕后,系统会立即回收匿名对象
  • 不要利用拷贝构造函数来初始化匿名对象,如:Person(p3);,编译器会将这句语句等价成Person p3;

3、隐式转化法

void test()
{
	/*1、括号法*/

	/*2、显示法*/

	/*3、隐式转换法*/
	Person p2 = 10;			//有参构造
	Person p3 = p2;			//无参构造

}

注意:

  • 在C++中有一个关键字explicit,可以用来修饰某个类的有参(拷贝)构造函数,被修饰的类将无法被隐式转换法创建、

例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:

	explicit Person(int a)		//加explicit修饰,防止隐式法创建对象
	{
	}
	Person(const Person& p)
	{
	}

	int m_age;
};

int main()
{
	Person p1(10);
	Person p2 = Person(10);
	//Person p3 = 10;			语法错误

	system("pause");

	return 0;
}

注:

  • 如果拷贝构造函数也用explicit关键字修饰,那么显示法也无法创建对象
拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常由三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传递
  • 以值方式返回局部对象

示例:

/*1、使用一个已经创建完毕的对象来初始化一个新对象*/
void test1()
{
	Person p1;
	Person p2(p1);
}

/*2、值传递的方式给函数参数传递*/
void func2(Person p)
{
}
void test2()
{
	Person p1;
	func2(p1);
}

/*3、以值方式返回局部对象*/
Person func3()
{
	Person p;
	return p;
}
void test3()
{
	Person p1 = func3();
}

总结:

  • 第一种情况是主动创建新对象(拷贝构造),这也是我们最容易理解的情况
  • 第二种情况是值传递的时候,被调用函数要在栈中新建一个对象副本(形参)
  • 第三种情况是函数返回对象,return的动作同样会在栈中创建临时变量
构造函数调用规则

默认情况下,C++编译器至少给一个类添加3个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,C++不再提供默认无参构造函数,但会提供默认拷贝构造函数
  • 如果用户定义拷贝构造函数,C++不会再提供其他构造函数

总结:

  • 默认提供的构造函数和析构函数都是空实现,什么事情都不会做。当用户编写了相应函数,则调用用户编写的函数。
  • 默认提供的拷贝构造函数会把类中的全部属性复制过来,如果有特殊需求则需要自己编写拷贝构造函数。
  • 本小节就是一些死规矩,记住即可。
深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:
	/*构造函数*/
	Person()				
	{
		cout << "Person无参构造函数调用" << endl;
	}
	Person(string name, int age)		
	{
		m_name = new string(name);
		m_age = age;
		cout << "Person有参构造函数调用" << endl;
	}
	//Person(const Person& p)		//这里我们使用默认的拷贝构造函数
	//{
	//}

	/*析构函数*/
	~Person()
	{
		delete m_name;				//释放动态内存
        m_name = NULL;
		cout << "Person析构函数调用" << endl;
	}

	/*属性*/
	int m_age;
	string* m_name;
};

int main()
{	
	Person p1("张三", 18);
	Person p2(p1);			//下面两句正常输出
	cout << "p1姓名:" << *p1.m_name << ",p1年龄:" << p1.m_age << endl;
	cout << "p2姓名:" << *p2.m_name << ",p2年龄:" << p2.m_age << endl;

    
	*p2.m_name = "李四";	   //修改一下p2的姓名
    /*结果发现p1的姓名也跟着被修改了*/
	cout << "p1姓名:" << *p1.m_name << ",p1年龄:" << p1.m_age << endl;
	cout << "p2姓名:" << *p2.m_name << ",p2年龄:" << p2.m_age << endl;

	system("pause");

	return 0;
}

在关闭程序窗口的时候还会报错:引发了异常: 读取访问权限冲突

原因分析:

  • 默认的拷贝构造函数会做浅拷贝操作,把对象属性的值一一复制过去。在本例中,Person类含有一个指针成员,用来指向一块从堆中申请的内存。在创建对象p1的时候,调用了有参构造函数,从堆中申请了一块内存,并让m_name指向这块内存。而在创建对象p2的时候,使用了默认的拷贝构造函数,p2中的m_name指针变量复制了p1中m_name指针变量的值,导致这两个指针都指向了同一块内存。所以其中一个对象修改名字后,另一个对象的名字也会跟着改变。

  • 同时,在退出main函数时,系统会释放栈中内存。因为p2后被创建,所以它先被销毁(栈先进后出),销毁时调用了析构函数,释放了堆中内存。然后当p1也被销毁时,同样要调用一次析构函数,此时堆中内存已经被p2释放掉了,p1又去释放,因而导致程序崩溃。

解决办法:深拷贝

重新编写一个拷贝构造函数,重新在堆区申请一块内存

Person(const Person& p)		
{
	m_name = new string(*p.m_name);
	m_age = p.m_age;
}
初始化列表

作用:C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2),属性3(值3)...{}

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:
	/*构造函数*/
	Person():m_name("张三"), m_age(18)
	{

	}

	/*属性*/
	string m_name;
	int m_age;	
};

int main()
{	
	Person p1;
	cout << "p1姓名:" << p1.m_name << ",p1年龄:" << p1.m_age << endl;


	system("pause");

	return 0;
}

初始化列表可以采用这样的形式:

Person(string name, int age):m_name(name), m_age(age)
{

}

调用时方法如下:Person p1("张三", 18);

但这样就和有参构造函数的功能重复了。

类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员

例如:

class A
{};
class B
{
    A a;
};

B类中有对象A作为成员,A为对象成员

那么创建B对象时,A与B的构造、析构顺序是谁先谁后?

示例:

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Phone
{
public:
	Phone():m_name("苹果")
	{
		cout << "手机构造函数调用" << endl;
	}
	~Phone()
	{
		cout << "手机析构函数调用" << endl;
	}

	string m_name;
};

class Person
{
public:
	Person() :m_name("张三")
	{
		cout << "人类构造函数调用" << endl;
	}
	~Person()
	{
		cout << "人类析构函数调用" << endl;
	}

	string m_name;
	Phone m_phone;
};

void test()
{
	Person p1;

	cout << p1.m_name << "拿着" << p1.m_phone.m_name << "手机" << endl;
}

int main()
{	
	test();

	system("pause");

	return 0;
}

输出:

手机构造函数调用
人类构造函数调用
张三拿着苹果手机
人类析构函数调用
手机析构函数调用

总结:

  • 当一个类包含其他类,创建此类对象的时候会先构造对象成员,再构造自身
  • 当销毁此对象的时候,会先析构自身,再析构对象成员
静态成员

静态成员就是在成员变量和成员函数前加上static关键字,称为静态成员。

静态成员分为:

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量

示例1:静态成员变量

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:
	static int m_a;		//类内声明

private:
	static int m_b;
};

int Person::m_a = 10;	//类外初始化
int Person::m_b = 100;

int main()
{
	/*案例1:p1、p2共享静态成员变量*/
	Person p1;
	cout << p1.m_a << endl;

	Person p2;
	p2.m_a = 20;
	cout << p1.m_a << endl;			//输出20

	/*案例2:通过类名访问静态成员变量*/
	Person::m_a = 30;
	cout << Person::m_a << endl;	//输出30
	
	/*案例3:静态成员变量的访问权限*/
	//Person::m_b = 200;			私有权限,类外无法访问

	system("pause");

	return 0;
}

总结:

  • 静态成员变量被所有此类对象共享内存
  • 静态成员变量可以使用类名来访问
  • 静态成员变量可以设置不同的访问权限

示例2:静态成员函数

#include <iostream>
#include <string>
#include <ctime>
using namespace std;

class Person
{
public:
	static void func()			//静态成员函数
	{
		cout << "静态成员函数调用" << endl;
		cout << "m_a = " << m_a << endl;
		//cout << "m_b = " << m_b << endl;	静态成员函数无法访问非静态成员变量
	}

	static int m_a;		//静态成员变量
	int m_b;			//非静态成员变量
};

int Person::m_a = 10;	

int main()
{
	/*案例1:通过对象访问静态成员函数*/
	Person p1;
	p1.func();

	/*案例2:通过类名访问静态成员函数*/
	Person::func();

	system("pause");

	return 0;
}

总结:

  • 静态成员函数无法访问非静态成员变量
  • 静态成员函数可以被类名访问
  • 静态成员函数同样有访问权限

对象模型和this指针

成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上

1、空对象占用的内存空间是多少?

#include <iostream>
#include <string>
using namespace std;

class Person
{
    
};

int main()
{
    cout << sizeof(Person) << endl;
    
    system("pause");

	return 0;
}

答案是1个字节:

  • C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
  • 每个空对象也应该有一个独一无二的内存地址

2、含有非静态成员变量的对象占用多少内存?

class Person
{
    int a;
};

答案是4字节。

3、含有静态成员变量的对象占用多少内存?

class Person
{
    int a;
    static int b;
};

答案还是4字节,因为静态成员变量不属于类对象上

4、含有非静态成员函数的对象占用多少内存?

class Person
{
    int a;
    static int b;
    
    void func()
    {}
};

答案依然是4字节,非静态成员函数不属于类对象上

5、含有静态成员函数的对象占用多少内存?

class Person
{
    int a;
    static int b;
    
    void func()
    {}
    
    static func2()
    {}
};

答案仍然是4字节

总结:

  • 只有非静态成员变量,属于类的对象上
  • 静态成员变量和成员函数都不属于类对象上,它们被所有的类对象共用一份
this指针概念

通过上一小节我们知道,在C++中成员变量和成员函数是分开存储的

每一个非静态成员函数只会诞生一份函数实例,也就是说多个类型的对象会共用一块代码

那么问题来了:这一块代码是如何区分是哪一个对象在调用自己?

C++通过提供特殊的对象指针:this指针,来解决这类问题。this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

this指针的用途

  • 当形参和成员变量同名时,可以用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this

示例1:解决成员函数的形参和成员变量重名的问题

class Person
{
public:
    void func(int a)
    {
        this->a = a;
    }
    
    int a;
};

示例2:返回对象本身用*this

#include <iostream>
#include <string>
using namespace std;

class Person
{
public:
    Person() :a(0), b(0)
    {}

    Person& addA(int a)		//返回引用
    {
        this->a += a;

        return *this;
    }

    Person addB(int b)		//返回对象
    {
        this->b += b;

        return *this;
    }

    int a;
    int b;
};

int main()
{
    Person p;

    p.addA(10).addA(10).addA(10);
    cout << "a = " << p.a << endl;

    p.addB(10).addB(10).addB(10);
    cout << "b = " << p.b << endl;

    system("pause");

    return 0;
}

输出:

a = 30
b = 10

总结:

  • 这叫链式编程思想,一个对象可以无限追加的调用方法
  • 想用这种方式,成员函数一定要引用返回。如果是值返回,每次返回都会创建一个新的对象,新的对象不是原来是对象p,因此累加的值不会加到p对象中的b上

结论:哪个对象调用的成员函数,此时this指针就指向该对象

空指针访问成员函数

C++中空指针也可以调用成员函数,但要注意成员函数中有没有用到this指针

如果用到了this指针,就需要加以判断保证代码的健壮性

示例:

#include <iostream>
#include <string>
using namespace std;

class Person
{
public:
    void show()
    {
        cout << "I am Person class" << endl;
    }

    void showAge()
    {
        /*访问m_age实际上等于访问this->m_age,如果是空指针调用,就会程序崩溃*/
        cout << "the age is:" << m_age << endl;
    }

    int m_age;
};

int main()
{
    Person* p = NULL;   //创建Person类型的空指针

    p->show();          //这句正常运行
    p->showAge();       //这句使程序崩溃

    system("pause");

    return 0;
}

改进措施:

class Person
{
public:
    void show()
    {
        cout << "I am Person class" << endl;
    }

    void showAge()
    {
        
        if (this == NULL)		//如果是空指针在访问,直接返回
            return;
        
        cout << "the age is:" << m_age << endl;
    }

    int m_age;
};
const 修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数
  • 常函数内不可以修饰成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数

示例:

#include <iostream>
#include <string>
using namespace std;

class Person
{
public:
    /* this 指针的本质是指针常量,像
    this = NULL; 这样的语句是不允许的*/
    void setB() const   //加const修饰,变为常函数
    {
        //this->m_a = 10; 报错,常对象不可以修改普通成员变量
        this->m_b = 10;     //常函数可以修改加 mutable 修饰的成员变量
    }

    void setA()
    {
        this->m_a = 10;
    }

    int m_a;
    mutable int m_b;
};

int main()
{
    const Person p;     //const修饰,变为常对象
    //p.m_a = 10;       报错,常对象不可以修改普通成员变量
    p.m_b = 10;         //常对象可以修改加 mutable 修饰的成员变量

    /*常对象只能访问常函数*/
    p.setB();
    //p.setA();         报错

    system("pause");

    return 0;
}

总结:

  • this指针的本质是指针常量:Person const *this = 调用成员函数的那个对象;。因此,修改this指针的指向是不可以的
  • 加const修饰的成员函数,就会使this指针指向的值也不可修改:const Person const *this = 调用成员函数的那个对象;

友元

​ 通过之前的学习我们了解到,如果一个类中的某些属性设置了私有权限,那么这些属性只能在这个类内被访问,在类外是无法访问这些私有属性的,更别说其他的类。但在本节,我们要学习一种新的特性,它可以使得某个函数或者类访问到某个类的私有属性,这个特性就是友元

友元特性使得不同的类不再是彼此孤立的状态,可以存在联系。

友元的关键词friend

友元的三种实现形式:

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元
全局函数做友元

示例:

#include <iostream>
#include <string>
using namespace std;

class Person
{
    /*这就是全局函数做友元的声明
    被声明的函数可以访问本类的保护、私有属性*/
    friend void goodFriend(Person& p);
    friend int main();

public:
    void init()
    {
        name = "张三";
        car = "奔驰";
        secret = "捡到一百块";
    }
    string name;
protected:
    string car;
private:
    string secret;
};

void goodFriend(Person &p)
{
    cout << "好友的姓名为:" << p.name << endl;
    cout << "好友的汽车为:" << p.car << endl;
    cout << "好友的秘密为:" << p.secret << endl;
}

int main()
{
    Person p1;
    p1.init();
    
    p1.car = "宝马";

    goodFriend(p1);

    system("pause");

    return 0;
}

输出:

好友的姓名为:张三                                                               好友的汽车为:宝马                                                               好友的秘密为:捡到一百块  
类做友元

示例:

#include <iostream>
#include <string>
using namespace std;

class Person
{
    /*这就是类做友元的声明,被声明的类可以
    通过它的所有成员函数访问本类的保护、私有属性*/
    friend class Friend;

public:
    /*添加构造函数,在创建后属性被赋值*/
    Person()
    {
        name = "张三";
        car = "奔驰";
        secret = "捡到一百块";
    }
    string name;
protected:
    string car;
private:
    string secret;
};

class Friend
{
public:
    /*添加构造函数,在堆区创建一个Person对象,
    并让goodFriend指针指向它*/
    Friend()
    {
        goodFriend = new Person;
    }

    /*Friend类通过成员函数访问Person类中的保护、私有属性*/
    void visitFriend()
    {
        cout << "好友的姓名为:" << goodFriend->name << endl;
        cout << "好友的汽车为:" << goodFriend->car << endl;
        cout << "好友的秘密为:" << goodFriend->secret << endl;
    }

    Person* goodFriend;
};


int main()
{
    Friend f1;

    f1.visitFriend();

    system("pause");

    return 0;
}

输出:

好友的姓名为:张三                                                               好友的汽车为:奔驰                                                             好友的秘密为:捡到一百块 
成员函数做友元

成员函数做友元就更简单了。一个类做友元,那么这个类中所有的成员函数都可以访问另一个类中的保护、私有属性(为什么只说成员函数能访问?不然呢,难道类中的变量也能访问吗)。而成员函数做友元,就只有被声明的成员函数能访问。

示例:

#include <iostream>
#include <string>
using namespace std;

class Person;
class Friend;

class Person
{
    /*这就是成员函数做友元的声明,被声明的
    成员函数可以访问本类的保护、私有属性*/
    friend void Friend::visitFriend();

public:
    /*添加构造函数,在创建后属性被赋值*/
    Person()
    {
        name = "张三";
        car = "奔驰";
        secret = "捡到一百块";
    }
    string name;
protected:
    string car;
private:
    string secret;
};

class Friend
{
public:
    /*添加构造函数,在堆区创建一个Person对象,
    并让goodFriend指针指向它*/
    Friend()
    {
        goodFriend = new Person;
    }

    /*Friend类通过成员函数访问Person类中的保护、私有属性*/
    void visitFriend()
    {
        cout << "好友的姓名为:" << goodFriend->name << endl;
        cout << "好友的汽车为:" << goodFriend->car << endl;
        cout << "好友的秘密为:" << goodFriend->secret << endl;
    }
    void visitFriend2()
    {
        cout << "好友的姓名为:" << goodFriend->name << endl;
      //  cout << "好友的汽车为:" << goodFriend->car << endl;
       // cout << "好友的秘密为:" << goodFriend->secret << endl;
    }

    Person* goodFriend;
};


int main()
{
    Friend f1;

    f1.visitFriend();

    system("pause");

    return 0;
}

结果发现添加了成员函数做友元的声明还是报错无法访问,编译不通过。

解决方法:

​ 后来发现,如果要让成员函数做友元,成员函数必须类内定义,类外实现。且做友元的那个类必须定义在前头,特别难搞。

#include <iostream>
#include <string>
using namespace std;

class Person;
class Friend;

/*Friend类必须定义在Person类的前面,不然会报错*/
class Friend
{
public:
    /*添加构造函数,在堆区创建一个Person对象,
    并让goodFriend指针指向它*/
    Friend();

    /*Friend类通过成员函数访问Person类中的保护、私有属性*/
    void visitFriend();
    void visitFriend2();

    Person* goodFriend;
};

class Person
{
    /*这就是成员函数做友元的声明,被声明的
    成员函数可以访问本类的保护、私有属性*/
    friend void Friend::visitFriend();

public:
    /*添加构造函数,在创建后属性被赋值*/
    Person()
    {
        name = "张三";
        car = "奔驰";
        secret = "捡到一百块";
    }

    string name;
protected:
    string car;
private:
    string secret;
};

/*这个构造函数同样要写在外头,不然报错*/
Friend::Friend()
{
    goodFriend = new Person;
}

void Friend::visitFriend()
{
    cout << "好友的姓名为:" << goodFriend->name << endl;
    cout << "好友的汽车为:" << goodFriend->car << endl;
    cout << "好友的秘密为:" << goodFriend->secret << endl;
}

void Friend::visitFriend2()
{
    cout << "好友的姓名为:" << goodFriend->name << endl;

    /*下两句会报错*/
    //cout << "好友的汽车为:" << goodFriend->car << endl;
    //cout << "好友的秘密为:" << goodFriend->secret << endl;
}

int main()
{
    Friend f1;

    f1.visitFriend();
    f1.visitFriend2();

    system("pause");

    return 0;
}

输出:

好友的姓名为:张三
好友的汽车为:奔驰
好友的秘密为:捡到一百块
好友的姓名为:张三

知识点补充

  • 友元的关系的单向的
  • 友元不能被继承
  • 友元不具有传递性
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面向对象的编程中,C语言并不直接支持和抽象的概念。引用中提到,final关键字用来修饰方法,表示该方法不能在子中被覆盖。而abstract关键字用来修饰抽象方法,表示该方法必须在子中被实现。然而,在C语言中,没有对应的关键字来实现和抽象的概念。 相反,C语言通过结构体来模拟的概念。结构体是一种用户自定义的数据型,可以包含多个不同型的数据成员。通过结构体,我们可以将相关的数据和功能组合在一起。然而,C语言中的结构体不支持继承和多态等面向对象的特性。 在C语言中,我们可以使用函数指针来模拟抽象和接口的概念。函数指针可以指向不同的函数,通过使用函数指针,我们可以实现多态性,即在运行时根据函数指针指向的具体函数来执行不同的操作。 综上所述,C语言并不直接支持面向对象中的和抽象的概念,但可以使用结构体和函数指针来实现似的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [面向对象——和对象](https://blog.csdn.net/shouyeren_st/article/details/126210622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [面向对象编程原则(06)——依赖倒转原则](https://blog.csdn.net/lfdfhl/article/details/126673771)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值