C++面向对象

一、引入

实现程序的目的------模拟现实
C语言:struct只有属性没有行为
在这里插入图片描述


struct Person
{
    const char* _name;
    int _sex;
    int _age;

    void (*eat)(struct Person* per);
    void (*run)(struct Person* per);
    void (*sleep)(struct Person* per);
    void (*show)(struct Person* per);
};//28个字节
void eat(struct Person* per)
{
    cout << per->_name << " eat eat eat ······" << endl;
}
void run(struct Person* per)
{
    cout << per->_name << " run run run ······" << endl;
}
void sleep(struct Person* per)
{
    cout << per->_name << " sleep sleep sleep ······" << endl;
}
void show(struct Person* per)
{
    cout << "name:" << per->_name << endl;
    cout << "sex:" << per->_sex << endl;
    cout << "age:" << per->_age << endl;
}
int main()
{


    struct Person per;
    per.eat = eat;
    per.run = run;
    per.sleep = sleep;
    per.show = show;

    per._name = (char*)malloc(strlen("zhangsan"+1));
   // per._name = new char[strlen("zhangsan" + 1)];
    strcpy_s(per._name, strlen("zhangsan") + 1, "shangsan");
    per._sex = 1;
    per._age = 23;

    per.show(&per);
    per.eat(&per);
    per.sleep(&per);
    per.run(&per);

    struct Person per1;
    per1._name = "lisi";
    return 0;
}

设想:

1.能否成员方法不占用空间

2.成员方法不用传递参数具体是哪个成员

3.对象死亡时候能否自动清理内存

4.对象生成能够更简单

二、C++面向对象

在类中定义成员方法,不占用内存;

不传参数,自己会识别那个对象调用。

1.构造函数,拷贝函数,运算符重载,析构函数

(1)构造函数

  • 构造函数没有返回值;
  • 构造函数函数名和类名相同;
  • 构造函数在对象生成的时候会自动调用;
  • 如果没有实现构造函数,系统会自己生成一个没有参数的构造函数,这个构造函数什么都不做;
  • 构造函数可以有多个,多个之间存在重载关系。

(2)拷贝函数

  • 拷贝构造函数是在用已存在的对象构造新生成的对象的时候自动调用;
  • 如果没有自己实现拷贝构造函数,系统会自动生成一个拷贝构造函数,只进行浅拷贝,什么都不做。

注意:防止浅拷贝;一定要传引用。

在这里插入图片描述
浅拷贝:_name=src._name

深拷贝:_name=new char[strlen(src.name)+1]
strcpy_s(name,strlen(src.name)+1,src._name)

传引用:Person(Person& src){}

(3)等号运算符重载

  • 等号运算符是用已存在的对象给已存在的对象进行赋值的过程自动调用的函数;
  • 如果没有自己实现的等号运算符重载函数系统会默认生成一个浅拷贝的等号运算符重载函数动态调用。

注意:防止自赋值;防止内存泄漏;防止浅拷贝

(4)析构函数

  • 在对象死亡的时候,自动调用的函数;
  • 如果没有自己实现的析构函数,系统会默认生成一个空的析构函数;
  • 先构造的后析构。

注意: 防止内存泄漏

class Person
{
public:
   char* _name;
   int _sex;
   int _age;


    /*
       系统默认C++在类中普通成员方法第一个参数是类的指针,不可以手动写
       类的普通成员方法的形参列表第一个参数是this指针,默认不感知,不可以手动写
       类的普通成员方法使用的成员前面默认加上this->,不感知,也可以手动写

   */
   //构造函数
   Person(/*Person *const this*/const char* name, int sex, int age)
   {
       _name = new char[strlen(name) + 1];
       strcpy_s(_name, strlen(name) + 1, name);
       _sex = sex;
       _age = age;
   }
   //没有参数的构造函数称为默认构造函数
   Person()
   {
       cout << "Person()" << endl;
   }
 
   //拷贝构造函数
   //要传&不能直接传对象,如果没有&,相当于Person src=per直接拷贝过程,从而进入死递归;
   Person(Person& src)
   {
        _name = new char[strlen(src._name) + 1];
       strcpy_s(_name, strlen(src._name) + 1, src._name);
       _age = src._age;
       cout << "Person(Person& src)" << endl;
   }
  
   //等号运算符重载
   Person& operator=(const Person& src)
   {
       cout << "Person& operator=(const Person& src)" << endl;
       //防止自赋值
       if (&src == this)
       {
           return *this;
       }
       //防止内存泄漏
       delete[]_name;
       //防止浅拷贝
       _name = new char[strlen(src._name) + 1];
       strcpy_s(_name, strlen(src._name) + 1, src._name);
       _sex = src._sex;
       _age = src._age;
       return *this;
   }

   //析构函数
   ~Person()
   {
       if (NULL != _name)
       {
           delete[]_name;
           _name = NULL;
           cout << "~Person()" << endl;
       }
   }
   void eat()
   {
       cout << _name << " eat eat eat ······" << endl;
   }
   void run()
   {
       cout << _name << " run run run ······" << endl;
   }
   void sleep()
   {
       cout << _name << " sleep sleep sleep ······" << endl;
   }
   void show()
   {
       cout << "name:" << _name << endl;
       cout << "sex:" << _sex << endl;
       cout << "age:" << _age << endl;
   }

};
int main()
{
   //生成对象的过程叫做构造,自动调用构造函数
   Person per(/*&per*/"zhangsan",32,1);


   //Person per1;//ok
   //Person per1();//error,编译器不知道是函数声明还是构造对象
   
   //用一个已经存在的对象构造一个正在生成的对象
   //叫做拷贝构造
   Person per1(per);//等价于Person per1=per
   Person per2 = per;
   //per.show(/*&per*/);
   //per.eat(/*&per*/);
   //per.run(/*&per*/);
   //per.sleep(/*&per*/);

   //cout << "==========================" << endl;
   //per1.show();
   //per._name[4] = '8';
   //per.show();
   //per1.show();

   //用已存在的对象给已存在的对象赋值
   per1 = per = per2;//赋值
   //per1.show();
   per = per;
   

}

小结:

c++中面向对象,系统自动做的事情:

1.自动调用构造函数

2.自动调用拷贝构造

3.自动调用等号运算符重载

4.自动调用析构函数

5.如果自动调用的函数不存在,就会默认生成该函数

6.普通成员方法的形参列表在第一个参数的位置默认加上this指针

7.在普通成员方法调用的地方,实参列表第一个默认加上this指针

8.在普通成员方法中使用到普通成员的地方默认加上this->

2.面向对象

抽象 封装

面向对象的三大特征:封装、继承、多态

面向对象的四大特征:抽象、封装、继承、多态

(1)访问权限

public:公有的
    
	   所有人都可以访问

	   一般对外提供的接口使用public
	   
private:私有的

    	只有类内部的成员方法可读可写
    	
    	类外不可见不可写
        
        除了必须对外提供的接口,其他的都写成私有


在class类中所有的成员默认的属性是私有-private;

在c++中,struct也是类,在struct类中所有成员的默认属性是公有的。


如何选择用class和struct:
    
如果简单的类,成员方法不多的一般用struct;class一般用于较大一点,功能较多的类。

除了必须对外提供的接口,其他的都写成私有;

对于外界需要访问成员属性也写成私有,在公有的权限中提供访问接口

(2)类中的static关键字

  • 成员属性加上static代表整个类只有这一份;
  • 静态成员属性必须在类外初始化(因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的);
  • 私有的static成员属性依然可以在外界进行初始化,但是私有的成员除了初始化,在外界其他地方不可以访问;
  • 私有成员的访问不必依赖对象,可以直接使用 类名::
  • 静态成员方法:使用不依赖于对象;访问可以直接使用 类名::;静态成员方法没有传入this指针,所以,静态成员方法内不可以访问普通成员
    在这里插入图片描述

(3)const

const 对象

常对象只能调用普通常方法和静态方法

常方法指的是在方法参数列表后括号后面加上const,这个const修饰的是this指针

建议:所有的普通成员方法如果方法内没有修改成员就将该方法写成常方法

const 成员属性

const成员属性必须放在初始化列表进行初始化

(4)初始化列表

只有构造函数有初始化列表;

const成员属性必须放在初始化列表;

引用成员属性必须放在初始化列表;

必须初始化成员必须放在初始化列表;

位置:在构造函数的参数列表下面,函数体的前{上面

为什么引用必须初始化?

因为编译时会在使用引用的地方全部替换成指针的解引用,所以引用在初始化后没有办法访问到引用本身,所以引用一经指向没有办法改变,必须初始化.

(5)单例模式

将构造函数写在私有的里面;

在共有的里面写一个静态的获取对象指针的方法;

将唯一的对象指针存储在私有的静态成员属性中,每次被获取。
class Stu
{
private:
    char* _name;
    const int _sex;
    int& _school;
    int _age;
    double _grade;
    static int _num;
    static Stu* p_stu;
	//单例模式
    Stu(const char* name, int sex, int school, int age, double grade)
        :_sex(sex), _school(school)
    {
        _name = new char[strlen(name) + 1];
        strcpy_s(_name, strlen(name) + 1, name);
        //_sex = sex;
        _age = age;
        _grade = grade;
        cout << "stu(const char* name, int sex, int age, double grade)" << endl;
    }
    
public:
	//单例模式接口
    static Stu* get_stu(const char* name, int sex, int school, int age, double grade)
    {
        if (NULL == p_stu)
        {
            p_stu = new Stu(name, sex, school, age, grade);

        }
        return p_stu;
    }
    static int get_num()
    {
        get_age();
        return _num;
    }
    //静态成员方法内无法访问普通成员
    static int get_age()
    {
        cout << "hahah" << endl;
        //show();//error
        //return this->_age;//error
    }
    Stu(int school)
        :_sex(0),_age(int()),_school(school)
    {
        _name = NULL;
       // _sex = 0;
        //_age = 0;//普通的也可以写到初始化列表,但const必须写
        _grade = 0;
        cout << "stu()" << endl;
    }
    /*
    Stu(const char* name, int sex, int school, int age, double grade)
        :_sex(sex),_school(school)
    {
        _name = new char[strlen(name) + 1];
        strcpy_s(_name, strlen(name) + 1, name);
        //_sex = sex;
        _age = age;
        _grade = grade;
        cout << "stu(const char* name, int sex, int age, double grade)" << endl;
    }
    */
    Stu(const Stu& src)
        :_sex(src._sex),_school(src._school)
    {
        _name = new char[strlen(src._name) + 1];
        strcpy_s(_name, strlen(src._name) + 1, src._name);
        //_sex = src._sex;
        _age = src._age;
        _grade = src._grade;
        cout << "stu(const stu& src)" << endl;
    }
    Stu& operator=(const Stu& src)
    {
        cout << "stu& operator=(const stu& src)" << endl;
        if (&src == this)
        {
            return *this;
        }
        if(NULL!=_name)
        {
            delete[]_name;
        }   
        _name = new char[strlen(src._name) + 1];
        strcpy_s(_name, strlen(src._name) + 1, src._name);
        _sex = src._sex;
        _age = src._age;
        _grade = src._grade;
        return *this;
    }
    ~Stu()
    {
        if (NULL != _name)
        {
            delete[]_name;
            _name = NULL;
        }
        cout << "~stu()" << endl;
    }
    void show(/*Stu* const this*/)const
    {
        //this = NULL;//error,this不让变
        cout << "name:" << _name << endl;
        cout << "sex:" << _sex << endl;
        cout << "age:" << _age << endl;
        cout << "grade:" << _grade << endl;
    }
	//如果外界需要访问,提供接口。通过成员方法修改属性,确保安全性
    void change_grade(int flage,double grade)
    {
        if (flage == 1)
        {
            _grade = grade;
        }
            
    }
    
    double get_grade()
    {
        return _grade;
    }
};
#if 0
int main()
{
    Stu stu; 
    Stu stu1("aaa", 12, 1, 45);
    stu= stu1;
    Stu stu2 = stu;
    stu.show();
    stu1.show();
    stu2.show();
    cout << stu._age << endl;
    stu._age = 10;
    return 0;
}
#endif

#if
int Stu::_num = 0;
Stu* Stu::p_stu = NULL;
int main()
{
    /*
    //cout << Stu::_num << endl;
    int n = Stu::get_num();
    Stu stu;
    stu.show();
    */

    /*
    
    const Stu stu("zhangsan", 1, 23, 5);
	
	//const Stu* p
    stu.show();//Stu* const this//把常量的地址给了一个非常量的指针,将方法改成:void show()const
    */

    Stu* stu = Stu::get_stu("zhangsan", 2, 1, 23, 5);
    Stu* stu2 = Stu::get_stu("zhangsan", 2, 1, 23, 5);


    return 0;
}

#endif // 1

3.通过链表实现单例模式的链栈

mlist.h

#ifndef LIST_H
#define LIST_H
#include
using namespace std;

struct Node
{
	int _val;
	Node* _next;
	Node(int val=int())
	{
		_val = val;
		_next = NULL;
	}
};

class List
{
public:
	List()
	{
		_head = new Node();
		_tail = _head;
	}
	//传参必须传引用,防止死递归
	List(const List& src)
	{
		_head = new Node();
		_tail = _head;
		Node* tmp = src._head->_next;

		while (NULL != tmp)
		{
			insert_tail(tmp->_val);
			tmp = tmp->_next;
		}

	}
	List& operator=(const List& src)
	{
		if (&src == this)
		{
			return *this;
		}
		while(!is_empty())
		{
			delete_head();
		}
		Node* tmp = src._head->_next;
		while (NULL != tmp)
		{
			insert_tail(tmp->_val);
			tmp = tmp->_next;
		}
		return *this;
	}
	~List()
	{
		while (!is_empty())
		{
			delete_head();
		}
		delete _head;
		_head = NULL;
		_tail = NULL;
	}

	bool is_empty()
	{
		return _head == _tail;
	}
	void insert_head(int val)
	{
		Node* p = new Node(val);
		p->_next = _head->_next;
		_head->_next = p;
		if (p->_next == NULL)
		{
			_tail = p;
		}

	}
	void insert_tail(int val)
	{
		Node* p = new Node(val);
		p->_next = NULL;
		_tail->_next = p;
		_tail = p;

	}
	int delete_head()
	{
		if (is_empty())
		{
			return 0;
		}
		Node* p = _head->_next;
		_head->_next = p->_next;
		
		if (_tail == p)
		{
			_tail = _head;
		}
		delete p;
		return 1;
	}
	int delete_tail()
	{
		if (is_empty())
		{
			return 0;
		}
		Node* tmp = _head;
		while (tmp->_next != _tail)
		{
			tmp = tmp->_next;
		}
		delete tmp->_next;
		tmp->_next = NULL;
		_tail = tmp;
		return 1;

	}
	int search(const int val)
	{
		Node* p = _head->_next;
		while (NULL != p && val != p->_val)
		{
			p = p->_next;
		}
		if (NULL == p)
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
	void show()
	{
		Node* p = _head->_next;
		while (NULL != p)
		{
			cout << p->_val << " ";
			p = p->_next;
		}
		cout << endl;
	}
	int get_first()
	{
		if (is_empty())
		{
			return -1;
		}
		return _head->_next->_val;
	}
	int get_last()
	{
		if (is_empty())
		{
			return -1;
		}
		return _tail->_val;
	}

private:
	Node* _head;
	Node* _tail;

};
#endif

mstack.h

#ifndef MASTACK_H
#define MSTACK_H
#include"mlist.h"
#include
using namespace std;

class Stack
{
public:
	static Stack* get_stack()
	{
		if (NULL == _p_stack)
		{
			_p_stack = new Stack();
		}
		return _p_stack;
	}
	void pop();
	void push(int val);
	int top();
	bool is_empty();
	void show();
private:
	//构造函数写到私有的里面,外界没有办法构造
	Stack();
	static Stack* _p_stack;
	List _list;
};

#endif

mstack.cpp

#include
#include"mlist.h"
#include"mstack.h"

//类外进行类内静态成员变量初始化
Stack* Stack::_p_stack = NULL;

Stack::Stack()
{

}
void Stack::pop()
{
	if (is_empty())
	{
		return;
	}
	_list.delete_head();
}
void Stack::push(int val)
{
	_list.insert_head(val);
}
int Stack::top()
{
	return _list.get_first();
}
bool Stack::is_empty()
{
	return _list.is_empty();
}
void Stack::show()
{
	_list.show();
}

main.cpp

#include
#include "mlist.h"
#include"mstack.h"
using namespace std;
#if 0
int main()
{
	List list;
	for (int i = 0; i < 10; i++)
	{
		list.insert_head(i);
	}
	list.show();
	for (int i = 0; i < 10; i++)
	{
		list.insert_tail(i);
	}
	list.show();

	int a = list.get_first();
	cout << a << endl;
	list.delete_head();
	list.show();


	int b = list.get_last();
	cout << b << endl;
	list.delete_tail();
	list.show();


	return 0;
}
#endif // 0
#if 1
int main()
{
	//Stack sta;
	Stack* sta = Stack::get_stack();
	for (int i = 0; i < 5; i++)
	{
		sta->push(i);
	}
	sta->show();
	cout << sta->top()<<endl;
	sta->pop();
	sta->show();

	return 0;
}
#endif // 1

4.访问私有成员的两种方式
(1)在public提供私有成员的接口;
(2)设置类或者函数友元(不建议,会暴露私有成员)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
类与类之间的关系

组合类:一个类是另一个类的一个组成部分
代理类:一个类的成员是另一个类成员的子集

类的编译程序:先编译类名,再编译成员名,再编译成员方法体

对象的生成顺序:先生成成员对象,再生成自身对象

对象析构顺序:先析构自身,再析构成员

成员对象如果没有默认的构造函数,则该成员对象的初始化必须手动放在初始化列表;如果成员对象有默认的构造函数,系统会自动再初始化列表加上该对象的默认构造。

5.

class Tmp
{
public:
	Tmp()
	{
		cout <<"Tmp() "<< endl;
	}
	Tmp(int a)
	{
		cout << "Tmp(int a) " << endl;
	}
	Tmp(const Tmp& src)
	{
		cout << "Tmp(const Tmp & src)" << endl;
	}
	Tmp& operator= (const Tmp & src)
	{
		cout << "Tmp& operator= (const Tmp & src)" << endl;
		return *this;
	}
	~Tmp()
	{
		cout<< "~Tmp()" << endl;
	}
};

(1)

int main()
{
	Tmp t3;
	t3 = 20;
	return 0;
}

在这里插入图片描述
(2)

int main()
{
	Tmp t7 = 30;
	return 0;
}

先构造临时对象,使用临时对象拷贝构造,析构临时对象
-------->会直接优化为构造目标对象
在这里插入图片描述
(3)

int main()
{
	const Tmp& tt = 40;//必须加const,否则会泄露常量的地址给非常量的指针
	cout << "===============" << endl;
	return 0;
}

使用40构造一个临时对象;把临时对象的引用给到tt

如果对临时对象进行引用,该临时对象的生命周期会扩大到和引用一致
在这里插入图片描述
(4)

class Tmp
{
public:
	Tmp()
	{
		cout <<"Tmp() "<< endl;
	}
	Tmp(int a)
	{
		_a = a;
		cout << "Tmp(int a) " << endl;
	}
	Tmp(const Tmp& src)
	{
		cout << "Tmp(const Tmp & src)" << endl;
	}
	Tmp& operator= (const Tmp & src)
	{
		cout << "Tmp& operator= (const Tmp & src)" << endl;
		return *this;
	}
	~Tmp()
	{
		cout<< "~Tmp()" << endl;
	}
	int _a;
};
const Tmp& fun()
{
	return 10;
}
int main()
{
	
	const Tmp& tt = fun();
	cout << endl;
	cout << tt._a << endl;
	return 0;
}

先根据10构造临时对象,该临时对象分配在fun()栈帧上,fun()结束后,临时对象的空间也被销毁(销毁指的是标记为未用空间),引用t成为了野指针。所以最后tt._a是随机值。
在这里插入图片描述
禁止返回临时对象||局部对象的引用或者指针
(5)

class Tmp
{
public:
	Tmp()
	{
		cout <<"Tmp() "<< endl;
	}
	Tmp(int a)
	{
		_a = a;
		cout << "Tmp(int a) " << endl;
	}
	Tmp(const Tmp& src)
	{
		cout << "Tmp(const Tmp & src)" << endl;
	}
	Tmp& operator= (const Tmp & src)
	{
		cout << "Tmp& operator= (const Tmp & src)" << endl;
		return *this;
	}
	~Tmp()
	{
		cout<< "~Tmp()" << endl;
	}
	int _a;
};
Tmp fun()
{
	Tmp t(10);	//构造t
	return t;//用t拷贝构造临时对象;析构t
}
int main()
{
	Tmp t1;
	//构造t1
	t1 = fun();
	/*
	使用 临时对象等号运算符 赋值给t1
	析构临时对象
	*/
	return 0;
}

调用的方法:
在这里插入图片描述
(6)加上传参

class Tmp
{
public:
	Tmp()
	{
		cout <<"Tmp() "<< endl;
	}
	Tmp(int a)
	{
		_a = a;
		cout << "Tmp(int a) " << endl;
	}
	Tmp(const Tmp& src)
	{
		cout << "Tmp(const Tmp & src)" << endl;
	}
	Tmp& operator= (const Tmp & src)
	{
		cout << "Tmp& operator= (const Tmp & src)" << endl;
		return *this;
	}
	~Tmp()
	{
		cout<< "~Tmp()" << endl;
	}
	int _a;
};
Tmp fun(const Tmp tt)
{
	Tmp t(10);
	/*
	用t拷贝构造临时对象
	*/
	return t;
	/*
	析构t
	析构tt
	*/
}
int main()
{
	Tmp t1;
	Tmp t2;
	//构造t1
	t2 = fun(t1);//使用t1拷贝构造tt
	return 0;
}

在这里插入图片描述
太复杂需优化。

建议:

  • 函数传递参数,能传引用就传引用,节省空间时间。如果确定函数中不会修改参数的值,就是用const引用
  • 如果要返回的数据是一个新产生的对象,可以返回临时量就返回临时量,并使用返回的临时量直接构造新的对象

优化结果:
在这里插入图片描述
(7)堆区申请对象

int main()
{
	Tmp* tp = new Tmp();
	return 0;
}

没有析构,进程结束才会释放空间,要手动析构delete tp
在这里插入图片描述

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