C++面向对象——类/抽象/构造函数/析构函数

  • 面向对象

概念:基于对象概念,以对象为中心,以类和继承为构造机制,来认识,理解,和刻画客观世界和设计构建相应的软件系统(模拟现实)
1、对象是由数据允许的操作组成的封装体,与客观事实有直接的对应关系
2、面向对象不是某种语言特性,而是一种编程思想
例:“今天我请人类吃饭”这种表达有点不正常的。“今天我请芸芸吃饭”这很正常。故称为面向对象

  • 抽象和类

抽象:从具体事务抽取共同的本质特征(例如人是由细胞组成,但是这样思考的话太复杂了,就看成整体就行,男人、女人)这种由复杂到简答来描述的过程就是抽象,即实体、属性集合、物种集合
类:1)类是一种将抽象转化为用户定义类型的工具(类如画家是用画板去画画,类就是代码,将抽象转换过来)。2)将数据表示和操纵数据的方法组合成一个整体。3)类的实例称为对象(实例就是调用类的方法,用类中方法(函数)去解决问题,这种封装好的方法就是实例化)。4)类中的属性(变量)、方法(函数)成为成员。(类就是属性+方法)。5)类的实例称为对象,对象的集合称为类

  • 概念理解了,下面说一下格式

注意:
1、class声明类型与struct声明类型 仅仅在形式上不同而已
2、唯一的区别在于与struct声明类型默认成员是私有的(private),而struct的默认类型是公有的(public)
*私有的必要性:女朋友,牙刷,手机等等私有物品

//类的声明
使用class/struct关键字声明类型
class 类名{};	//class Landowner{};
struct 类名{};	//struct Hero{};

//修饰符

//修饰成员:将修饰关键字放置在类定义的大括号中添加冒号
//属性解释
public		//修饰的成员在任何地方都可以访问
private 	//修饰的成员只能在类中或者友元函数中可以访问
protected 	//修饰的成员可以在类中函数、子类函数、及友元函数中访问

修饰符下的属性变量可以通过方法(函数)判断的方法,进行封装,封装就是用函数封装,函数声明和函数定义都在public中进行.

private:
	string gender;					//也可以加下划线,不过加不加无所谓,共识是加上画线表示私有变量
public:
	int age;						//同样的方法进行封装,这里就不举例

	//对属性封装不需要声明函数,直接定义函数对gender属性进行封装
	void Set_gender(string input_gender)//定义函数(封装体)
	{
		if(input_gender != "male" && input_gender != "female")
		{
			gender = "保密";
		}
		else
		{
			gender = input_gender;
		}
	}

//函数定义(方法)

格式和普通的函数一样,需要函数声明和函数定义,不同的是函数定义时要加上双冒号::作用域解析运算符
1、类的成员函数:函数声明(在头文件中)

class 类名{
	修饰符:
		成员列表;
		函数声明;
};
例如:
class Landowner{
	public:
		string name;
		void sum1(int, int);//在头文件定义类中声明函数,在cpp文件中定义函数
} 

2、类的成员函数:函数定义(在cpp文件中)

返回值类型 类名::函数名(){}
例如
void Landowner::sum1(int num1, int num2)//加上作用域解析符
{
	int result = num1 + num2;
}

  • 构造函数与析构函数

形象说法:对象在出生时调用构造函数,在回收时调用析构函数

//举个栗子
//头文件中
class Students
{
	public:
		Students(string,string);//构造函数的声明
		~Students();//析构函数,总是成双成对的出现
		void showinfo();//声明函数
	private:
		_name = name;
		_desc = desc;

};
//在cpp文件中
//传参方式1
Student::Students(string name, string desc)//构造函数定义
{
	_name = name;
	_desc = desc; 
}
/*
//传参方式2
Student::Students(string name, string desc):_name(name),_desc(desc); 
//传参还可以赋默认值,设置默认值,外部的输入无效,打印的是默认值
Student::Students(string name, string desc):_name(name),_desc("666"); 
*/
void Students::showinfo()//显示信息函数
{
	cout << "name: " << name << endl;
	cou << "describe" << desc << endl;
}
//在主文件中main.cpp
Students stu1(别名) = Students("大卫", "帅气") ;
stu1.showinfo();		//非指针调用直接用点
Students* stu2 = new Students("贝尔", "牛逼的");
stu2->showinfo();		//指针调用用的箭头

//析构函数会自动调去释放栈内存的变量
delete stu2;//堆内存的变量需要手动删除释放

//上代码

main.cpp

#include <iostream>
#include "Students.h"
using namespace std;

int main()
{
    //构造函数的重载规则跟普通函数的重载规则相同,重载的时候不关注的他的返回值,只关注返回类型
    Students stu1;//在栈内存中直接实例化
    Students stu2("马化腾","真有钱");//
    Students stu3(20);
    Students stu4("马云", "真有钱");
   // Students stu5 = 100;
    //Students stu6 = "牛逼轰轰轰";//这里会进行报错,说不匹配int类型,不能同时匹配int和string
    stu2.showinfo();

    //另外一种调用方式是通过指针的方式调用对象
    Students* stu7 = new Students("杰克马","真牛逼!!"); //创建一个指针,指向这个类,调用这个方法
    stu7->showinfo();//对于指针类型访问属性方法用->实现
    delete stu7;
    return 0;
}

Students.h

#ifndef STUDENTS_H
#define STUDENTS_H
#include <iostream>
using namespace std;

class Students
{
    public:
        //多个构造函数重名的原因是支持构造函数重载
        Students();//默认构造函数,通过构造函数的方法需要用双冒号指明作用域解析运算符
        Students(int );//自定义构造函数,并指明参数类型年龄
        Students(string);//这样会报错,会导致不匹配第一个int型,即构造函数若指定一个参数类型时,不支持再指定一个参数不同类型
        Students(string, string);//自定义带参构造
        ~Students();//析构函数

        void showinfo();
        string Getname()
        {
            return _name;
        }
        void Setname(string val)
        {
             _name = val;
        }
        string Getdesc() {
             return _desc;
              }
        void Setdesc(string val) {
            _desc = val;
            }
        int Getage() {
             return _age;
             }
        void Setage(int val)
        {
            if(val < 0)
            {
                _age = 18;
            }
            else
            {
                _age = val;
            }
        }

    protected:

    private:
        string _name;
        string _desc;
        int _age;
};

#endif // STUDENTS_H

Students.hpp

/*
*创建时间:
*描    述:
*作    者:
*/
#include <iostream>
using namespace std;
#include "Students.h"

Students::Students()//默认的初始化参数
{
    cout << "这个是默认的构造" << endl;
}

Students::~Students()//析构函数负责释放内存空间
{
    cout << _name << "被释放啦" << endl;

}
//自定义初始化参数
Students::Students(string name, string desc)//:_name(name),_desc(desc)//方法二:初始化参数列表的写法,
{
    _name = name;
    _desc = desc;
    cout << "这个是string name/desc构造" << endl;
}
Students::Students(int age)
{
    Setage(age);
    cout << "这个是int age构造" << endl;
}
Students::Students(string name)
{
    cout << "这个是单个string构造" << endl;

}
void Students::showinfo()//在类里面进行性函数定义,要加上作用域解析运算符
{
    cout << _name << _desc <<endl;
}


//程序输出

这个是默认的构造
这个是string name/desc构造
这个是int age构造
这个是string name/desc构造
马化腾真有钱
这个是string name/desc构造
杰克马真牛逼!!
杰克马被释放啦
马云被释放啦
被释放啦
马化腾被释放啦
被释放啦

Process returned 0 (0x0)   execution time : 0.092 s
Press any key to continue.

构造函数小结:

1、语法:名字和类名相同,没有返回值
2、作用:处于初始化对象
3、构造函数也可以重载
4、默认构造很重要,没有默认构造,则无法构造数组
5、如果一个类没有构造函数、析构函数,则编译器隐含的生成为其添加一个

对于成员的初始化和析构:

1、当对象被构造时,成员变量也被构造(成员函数的构造函数被调用)
2、当对象被析构时,成员变量也被析构(成员函数的析构函数被调用)

  • democlass.cpp
/*
*创建时间:
*描    述:
*作    者:阿代
*/
#include <iostream>
using namespace std;

class Other
{
public:
    Other()//默认构造
    {
        cout << "Other被创建 " << endl;
    }
    ~Other()//默认析构
    {
        cout << "Other被析构 " << endl;
    }
};

class Child
{
public:
    Child()//默认构造
    {
        cout << "Child被创建 " << endl;
    }
    ~Child()//默认析构
    {
        cout << "Child被析构 " << endl;
    }
};


class Object
{
public:
    Object()//默认构造
    {
        cout << "Object被创建 " << endl;
    }
    ~Object()//默认析构
    {
        cout << "Object被析构 " << endl;
    }
private:
    Child m_child;//这里Child是一个类,作为Object的成员变量
    Other m_other;
};
  • main.cpp
#include <iostream>
using namespace std;

#include "democlass.cpp"

int main()
{

    {//代码块表示出了块生命周期就结束
        Object obj;
    }
	return 0;

}

//程序输出

Child被创建
Other被创建
Object被创建
Object被析构
Other被析构
Child被析构

Process returned 0 (0x0)   execution time : 0.113 s
Press any key to continue.

可以很明显的看出,对像对成员变量构造的顺序:成员变量先被构造,对象后被构造,在析构顺序上刚好和构造函数相反
对于两个成员函数,则是按照先后顺序进行的构造

1. 请创建一个抽象类DataStructure,该包括下面的成员变量和成员函数: 1) 一个成员变量len,表示里面的元素个数最大值 2) 构造函数DataStructure(int l),将len初始化为0 3) 虚析构函数~DataStructure() 4) 纯虚函数Output(),输出DataStructure中的数据 5) 纯虚函数Size(),返回DataStructure中的元素个数 2. 请创建DataStructure的一个派生MyString,该包括下面的成员变量和成员函数: 1) 一个成员变量char* data,表示里面的数据 2) 构造函数MyString(int max_size),将MyString初始化为空串,最大元素个数为max_size 3) 析构函数~MyString(),释放相应的数据 4) Input()函数,往MyString输入数据 5) 重载operator+=()函数,实现两个字符串的连接 6) 重定义Output()和Size()函数 3. 请创建DataStructure的一个派生MyStack,该包括下面的成员变量和成员函数: 1) 一个成员变量int* data,用于里面的数据 2) 一个成员变量int top,表示最上面的元素下标 3) 构造函数MyStack(int max_size),将MyStack初始化为空栈,最大元素个数为max_size 4) 析构函数~MyStack(),释放相应的数据 5) Push_back(int e)函数,往栈里面压入一个数据e 6) 重定义Output()和Size()函数 4. 请编写main函数,测试上面程序的正确性 1) 创建两个MyString的对象str1和str2,分别调用Input函数输入str1和str2,然后分别调用operator+=函数将str2连接到str1的末尾 2) 创建一个MyStack的对象stack,调用Push_back函数输入往stack中输入m(m < max_size)个数据 3) 创建一个长度为3的DataStructure*型的数组,将其3个元素分别指向str1, str2, stack,然后编写for循环调用Size()和Output()函数输出每个元素的大小和内容。 5. 输入输出样例: 1) 输入样例 A promising techni que for checking reachability 4 12 23 34 45 2) 输出样例 47 A promising technique for checking reachability 29 que for checking reachability 4 12 23 34 45
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值