C++:随笔6---new\delete\虚方法\抽象方法

指针是一种专门用来存储内存地址的数据类型。(他存储的不是具体的数据而是别人的地址)

常用做法是:创建一个变量,再把这个变量的地址赋值给一个指针,然后就可以用指针去访问这个变量的值。

事实上在C和C++中,我们完全可以在没有创建变量的情况下,为有关数据分配内存,也就是直接创建一个指针,并让她指向新分配的内存块。

int *pointer=new int;//int *pointer表示一个指向整型变量的指针,指针的名字叫做pointer,我们用new给他创建一个内存这个内存是什么类型呢?是int型。(整型的内存在现在的编译器一般是占用了4个字节的内存空间,那么这样定义的语句就是说声明一个指针变量,这个指针变量的名字叫做pointer,它指向一个整型的地址空间,用new给他创建出来,new事实上就是那个malloc这个函数的一个进化版本,都是差不多的只是对他进行了进一步的封装而已)
*pointer=110;//接着给这个指针,就是new出来的这块内存给他赋值,赋值为110
std::cout<<*pointer;//使用这个指针变量的值把他给打印出来
delete pointer;//最后删除这个指针,也就是释放了这一块new出来的内存。
//最后一步是非常必要和关键的,这是因为C和C++程序不会自动释放内存,程序中的每一个new操作都必须有一个与之对应的delete操作。

创建对象有两种写法,一种就是这里的如下,一种就是使用 new。

就死记住,语法这样写:

类 对象 (参数);//括号内的参数会自动调用构造函数对这个类进行初始化。

http://c.biancheng.net/view/2221.html

#include<string>
#include<iostream>

class Pet
{
public:
	Pet(std::string theName);//构造函数
	void eat();
	void sleep();
	void play();
protected:
	std::string name;
};
class Cat :public Pet
{
public:
	Cat(std::string theName);
	void climb();
	void play();//对基类play的覆盖(在原有的基础上进行覆盖)
};
class Dog :public Pet 
{
public:
	Dog(std::string theName);
	void bark();
	void play();//对基类play的覆盖
};
Pet::Pet(std::string theName)
{
	name = theName;
}
void Pet::eat()
{
	std::cout<<name<<"正在吃东西"<<std::endl;
}
void Pet::sleep()
{
	std::cout << name << "正在睡觉" << std::endl;
}
void Pet::play()
{
	std::cout << name << "正在玩耍" << std::endl;
}
Cat::Cat(std::string theName):Pet(theName)//Cat的构造器继承Pet的构造器
{
}
void Cat::climb()
{
	std::cout << name << "正在爬树" << std::endl;
}
void Cat::play()
{
	Pet::play();
	std::cout << name << "正在玩球" << std::endl;
}
Dog::Dog(std::string theName):Pet(theName)//Cat的构造器继承Pet的构造器
{
}
void Dog::bark()
{
	std::cout << name << "正在叫" << std::endl;
}
void Dog::play()
{
	Pet::play();
	std::cout << name << "正在追赶那只猪" << std::endl;
}
int main()
{
	//第一种方法:创建对象
	//Cat cat("加菲猫");//这个Cat类实例化一个对象cat,取名叫做“加菲猫”。
	//Dog dog("小灰灰");
	//cat.eat();//使用指针的话就要用到->操作,而不能使用结构的.操作(点操作)。
	//cat.sleep();
	//cat.play();//先调用基类play()函数,再调用子类的play()函数
	//cat.climb();//此种创建对象方式可以使用

	Dog子类对象dog
	//dog.eat();
	//dog.sleep();
	//dog.play();//先调用基类play()函数,再调用子类的play()函数
	//dog.bark();//此种创建对象方式可以使用

	//第二种方法:指针方式创建对象
	//定义一个指针变量的形式来对它进行初始化
	Pet *cat = new Cat("加菲猫");//运行的时候才调用new,分配的是Cat类型的指针,给了cat,(为什么它可以用Pet来接受呢?因为Cat继承于Pet,所以使用Pet接受是没有问题的)
	Pet *dog = new Dog("小灰灰");//同上
	//Cat子类对象cat
	cat->eat();//使用指针的话就要用到->操作,而不能使用结构的.操作(点操作)。
	cat->sleep();
	cat->play();//此种方法创建对象:只调用基类play()函数,不再调用子类的play()函数
	//cat->climb();//此种Pet *cat = new Cat("加菲猫");创建对象方式:不能调用子类的该方法(但是我们明明在Cat子类里对play方法进行了覆盖)

	//Dog子类对象dog
	cat->eat();
	cat->sleep();
	cat->play();//此种方法创建对象:只调用基类play()函数,不再调用子类的play()函数。((但是我们明明在Dog子类里对play方法进行了覆盖,但实际上调用的是基类的play方法Pet::play()而不是两个覆盖的子类中的play()方法))
	//cat->bark();//此种Pet *dog = new Dog("小灰灰");创建对象方式:不能调用子类的该函数

	delete cat;
	delete dog;

	return 0;
}

该最佳点就是速度最优内存最节省的最佳点。

因为cat和dog都是Pet类型的指针,肯定会调用Pet类型的方法,这样子优化之后执行起来的速度才是最快的。

  

扫描二维码关注公众号,回复: 11962663 查看本文章

虚方法的版本:(只需要在基类里边的play()函数前边加上virtual关键字)

#include<string>
#include<iostream>

class Pet
{
public:
	Pet(std::string theName);//构造函数
	void eat();
	void sleep();
	virtual void play();//前边加上关键字virtual变成虚函数
protected:
	std::string name;
};
class Cat :public Pet
{
public:
	Cat(std::string theName);
	void climb();
	void play();//对基类play的覆盖(在原有的基础上进行覆盖)
};
class Dog :public Pet 
{
public:
	Dog(std::string theName);
	void bark();
	void play();//对基类play的覆盖
};
Pet::Pet(std::string theName)
{
	name = theName;
}
void Pet::eat()
{
	std::cout<<name<<"正在吃东西"<<std::endl;
}
void Pet::sleep()
{
	std::cout << name << "正在睡觉" << std::endl;
}
void Pet::play()
{
	std::cout << name << "正在玩耍" << std::endl;
}
Cat::Cat(std::string theName):Pet(theName)//Cat的构造器继承Pet的构造器
{
}
void Cat::climb()
{
	std::cout << name << "正在爬树" << std::endl;
}
void Cat::play()
{
	Pet::play();
	std::cout << name << "正在玩球" << std::endl;
}
Dog::Dog(std::string theName):Pet(theName)//Cat的构造器继承Pet的构造器
{
}
void Dog::bark()
{
	std::cout << name << "正在叫" << std::endl;
}
void Dog::play()
{
	Pet::play();
	std::cout << name << "正在追赶那只猪" << std::endl;
}
int main()
{
	//第一种方法:创建对象
	//Cat cat("加菲猫");//这个Cat类实例化一个对象cat,取名叫做“加菲猫”。
	//Dog dog("小灰灰");
	//cat.eat();//使用指针的话就要用到->操作,而不能使用结构的.操作(点操作)。
	//cat.sleep();
	//cat.play();//先调用基类play()函数,再调用子类的play()函数
	//cat.climb();//此种创建对象方式可以使用

	Dog子类对象dog
	//dog.eat();
	//dog.sleep();
	//dog.play();//先调用基类play()函数,再调用子类的play()函数
	//dog.bark();//此种创建对象方式可以使用

	//第二种方法:指针方式创建对象
	//定义一个指针变量的形式来对它进行初始化
	Pet *cat = new Cat("加菲猫");
	Pet *dog = new Dog("小灰灰");
	//Cat *cat = new Cat("加菲猫");
	//Dog *dog = new Dog("小灰灰");
	//Cat子类对象cat
	cat->eat();//使用指针的话就要用到->操作,而不能使用结构的.操作(点操作)。
	cat->sleep();
	cat->play();//此种方法Pet *cat = new Cat("加菲猫");创建对象再加上基类虚函数的方法:先调用基类play()函数,再调用子类的play()函数
	cat->climb();//这种Cat *cat = new Cat("加菲猫");创建对象方式:可以调用子类的该方法(但是我们明明在Cat子类里对play方法进行了覆盖)

	//Dog子类对象dog
	dog->eat();
	dog->sleep();
	dog->play();//此种方法创建对象Pet *dog = new Dog("小灰灰");再加上基类虚函数的方法:先调用基类play()函数,再调用子类的play()函数
	dog->bark();//这种Dog *dog = new Dog("小灰灰");创建对象方式:可以调用子类的该函数((但是我们明明在Dog子类里对play方法进行了覆盖,但实际上调用的是基类的play方法Pet::play()而不是两个覆盖的子类中的play()方法))

	delete cat;
	delete dog;

	return 0;
}

Tips:

  

------抽象方法------

它实际上像一个接口(接口就类似于像C中的printf()函数输出到桌面上,因为我们只需要根据这个函数所要求的参数提示把他写进去他就可以实现输出了,但是它内部是如何调用显卡等的我们不用管,所以printf可以认为是一个接口);那么什么时候要实现它呢?当我们的子类继承到它的时候,然后我们需要运用到这个接口的时候,才对他进行具体的实现。

因为下边会有不同的动物,玩法也不同,所以在基类里边就不需要对这个方法进行具体的实现。

因为在这个类里边他是没有实现的,它是一个抽象的方法,没有实现的只有在继承的时候才来实现它。

class Pet
{
public:
	Pet(std::string theName);//构造函数
	virtual void eat();//虚函数
	virtual void sleep();//虚函数
	virtual void play()=0;//定义为抽象的方法。下边不用对它进行实现
protected:
	std::string name;
};
class Cat :public Pet
{
public:
	Cat(std::string theName);
	void climb();
	void play();//对基类play的覆盖(在原有的基础上进行覆盖)
};
class Dog :public Pet 
{
public:
	Dog(std::string theName);
	void bark();
	void play();//对基类play的覆盖
};
Pet::Pet(std::string theName)
{
	name = theName;
}
void Pet::eat()
{
	std::cout<<name<<"正在吃东西"<<std::endl;
}
void Pet::sleep()
{
	std::cout << name << "正在睡觉" << std::endl;
}
//void Pet::play()
//{
//	std::cout << name << "正在玩耍" << std::endl;
//}//因为它是一个抽象的方法所以这里不用给他写实现的东西
Cat::Cat(std::string theName):Pet(theName)//Cat的构造器继承Pet的构造器
{
}
void Cat::climb()
{
	std::cout << name << "正在爬树" << std::endl;
}
void Cat::play()
{
	Pet::play();
	std::cout << name << "正在玩球" << std::endl;
}
Dog::Dog(std::string theName):Pet(theName)//Cat的构造器继承Pet的构造器
{
}
void Dog::bark()
{
	std::cout << name << "正在叫" << std::endl;
}
void Dog::play()
{
	Pet::play();
	std::cout << name << "正在追赶那只猪" << std::endl;
}
//下边主函数同上

PS总结:抽象方法就是在基类里边不用对它进行写实现的方法,就是虚方法=0即可;

--------多态-------

class ClxBase
{
  public:
     ClxBase()
      {

      }
     virtual ~ClxBase()
      {

      }
     virtual void dosomething()//虚函数,为了方便下边的覆盖调用
     {
         std::cout<<"Do something in class ClxBase"<<std::endl;
     }

};
class ClxDerived:public ClxBase
{
 public:
    ClxDerived()
      {

      }
    ~ClxDerived()
      {
           std::cout<<"Output from the destructor of class ClxDerived"<<std::endl
      }
    void dosomething()
     {
         std::cout<<"Do something in class ClxDerived"<<std::endl;
     }

};
int main()
{
    ClxBase *pTest=new ClxDerived;//进行一个声明和定义(这里new出来的是一个ClxDerived对象,给了pTest,)(事实上这个对象就是指向子类的)
    pTest->dosomething();//然后pTest指向dosomething
    delete pTest;
    return 0;
}

析构器都是虚方法。

#include<string>
#include<iostream>

class ClxBase
{
public:
	ClxBase()
	{

	}
	//virtual ~ClxBase()
	//{

	//}//带virtual的析构函数,下边输出结构会调用子类的析构函数如左下图
    ~ClxBase()
	{

	}//不带virtual的析构函数,下边输出结构不会调用子类的析构函数如右下图(后边ClxDerived子类的析构函数根本就没有被执行到)
	virtual void dosomething()//虚函数,为了方便下边的覆盖调用
	{
		std::cout << "Do something in class ClxBase" << std::endl;
	}

};
class ClxDerived :public ClxBase
{
public:
	ClxDerived()
	{

	}
	~ClxDerived()
	{
		std::cout << "执行了子类的析构函数" << std::endl;
	}
	void dosomething()
	{
		std::cout << "Do something in class ClxDerived" << std::endl;
	}

};
int main()
{
	ClxBase *pTest = new ClxDerived;//进行一个声明和定义(这里new出来的是一个ClxDerived对象,给了pTest,)(事实上这个对象就是指向子类的)
	pTest->dosomething();//然后pTest指向dosomething//(如果基类的析构不带virtual的话,意思就是它调用了这个子类,但是没有对这个子类进行析构,这是非常非常危险的)
	delete pTest;
	return 0;
}

结果展示:

  

//(如果基类的析构不带virtual的话,意思就是它调用了这个子类,但是没有对这个子类进行析构,这是非常非常危险的)

1内存泄漏就是内存申请了没有进行释放。2虚方法的作用就是使得我们的编译器变聪明.

如果我们根本就不必要在基类里边对它写实现我们就把他写成抽象函数也就是纯虚函数=0就行了,(为什么说基类才用写虚函数,因为虚函数所继承的函数他自动也会变成虚函数的,所以我们只用在基类里边写就行了)

猜你喜欢

转载自blog.csdn.net/m0_37957160/article/details/108995576
今日推荐