C++学习笔记之 类 对象 继承

一、类 对象 继承

1. 类和对象

1.1 C++在C的基础上增加了面向对象编程,类和对象就是 面向对象编程中 的核心概念
1.2 类是C++的一个核心特性(类就是用户自定义的类型),类中包含数据元素(变量)和处理数据元素的方法(函数),类中的数据和函数被称为类的成员
1.3 类提供了对象的蓝图,一般来说对象是根据类创建的。打个比方,类相当于房屋设计图,对象相当于根据设计图设计的房子
1.4 类的基础使用示例:由于成员的属性为public , 因此可以使用 直接成员访问运算符 . 进行访问

#include <iostream>
using namespace std;
 
class Fruit 
{
	public:
		string name;
   		int quatity;
   		float price;
   		float getTotalPrice();
   		void setPara(string n,int q,float p); 
};

float Fruit::getTotalPrice()  //通过 :: 指定范围,类的成员函数可以在类中定义,也可以在类外定义 
{
	return quatity*price;
}
void Fruit::setPara(string n,int q,float p)
{
	name=n; quatity=q; price=p;
}

int main( )
{
	Fruit Apple,Banana; 
	float totalPrice;
	
	Apple.name="Apple"; Apple.quatity=10; Apple.price=2.0; //直接成员访问运算符 . 
	totalPrice=Apple.quatity * Apple.price;
	cout<<Apple.name<<"'s total price is: "<<totalPrice<<endl;
	
	Banana.setPara("Banana",20,1.5);
	totalPrice=Banana.getTotalPrice();
	cout<<Banana.name<<"'s total price is: "<<totalPrice<<endl;
	
	return 0;

}

2. 数据封装 public/protected/private

2.1 数据封装是面向对象编程的一个重要特点,用于防止函数直接访问类的内部成员。类成员的访问限制 通过在类内部 使用关键字 public, private, protected 进行标记来控制,默认访问修饰符是private
2.2 公有成员 public 可以直接访问,在1.4的代码示例中已经演示
2.3 私有成员 private 在类的外部不可访问、不可查看,若不指定,默认情况下类的所有成员都是private 。实际操作中,在私有区域定义数据,在公有区域定义相关函数,以便在类的外部调用这些函数

#include <iostream>
using namespace std;
 
class Fruit 
{
	public:
		string name;
   		int quatity;
   		
   	public:
	   	float getCostPrice()
	   	{
	   		return CostPrice;
		}
	   	void setCostPrice(float cost)
	   	{
	   		CostPrice=cost;
		}
	   	
	private:
		float CostPrice;
};

int main( )
{
	Fruit Orange; 
	float totalCostPrice;
	
	Orange.name="Orange"; Orange.quatity=10; //无法通过 Orange.CostPrice进行访问 
	Orange.setCostPrice(3.12);
	cout<<Orange.name<<"'s cost price is "<<Orange.getCostPrice();
	cout<<" and the total cost price is "<<Orange.getCostPrice()*Orange.quatity;
	
	return 0;

}

2.4 受保护成员 protected , protected 和 private几乎相同,除了protected 可以被子类/派生类访问,如下所示

#include <iostream>
using namespace std;
 
class Fruit 
{ 	
	protected:
		float CostPrice;
	private:
		float MinCost;
};

class HardShellFruit:Fruit //HardShellFruit是继承自Fruit的子类 
{
	public:
		void setHardShellCostPrice(float cost)
		{
			CostPrice=cost;
			//MinCost=cost; 会触发报错,因为MinCost是private 
		} 
		float getHardShellCostPrice(void)
		{
			return CostPrice;
		}
}; 

int main( )
{
	HardShellFruit mangosteen; 
	
	mangosteen.setHardShellCostPrice(16.1);
	cout<<"Cost price of mangosteen is "<<mangosteen.getHardShellCostPrice()<<endl; 
	
	return 0;
}

3. 类的函数

3.1 构造函数简介

构造函数是类的一种特殊成员函数,在每次创建对象时执行。构造函数的名称与类的名称完全相同,不会返回任何类型(包括void)。构造函数默认不带参数,当构造函数带参数时,可用于为部分成员变量赋初值

#include <iostream>
using namespace std;
 
class Fruit 
{
	public:
		string name;
   		int quatity;
   		float CostPrice;
   	
   	public:
	   	Fruit(int qua,float cost) //构造函数
		{
			cout<<"Object is being created, the default quatitiy is "<<qua;
			cout<<" , the default CostPrice is "<<cost<<endl;
			quatity=qua;
			CostPrice=cost;
		}
	   	float getCostPrice()
	   	{
	   		return CostPrice;
		}	
};

int main( )
{
	Fruit strawberry(100,5.1); 
	cout<<"the strawberry's default costprice is "<<strawberry.CostPrice;
	cout<<endl;
	strawberry.CostPrice=5.3;
	cout<<"the strawberry's current costprice is "<<strawberry.getCostPrice();
	
	return 0;

}

3.2 构造函数之使用初始化列表来初始化字段

示例如下,A B两种写法是等价的,A ==Fruit::Fruit(float cost):CostPrice(cost){ } ==就是使用初始化列表来初始化字段

Fruit::Fruit(float cost):CostPrice(cost){ } //A


Fruit::Fruit(float cost) //B
{
	CostPrice=cost;
}

当需要初始化多个成员变量时,通过逗号分隔即可

Fruit::Fruit( int qua, float cost): quatity(qua), CostPrice(cost){}

完整编码如下

#include <iostream>
using namespace std;
 
class Fruit 
{
	public:
		string name;
   		int quatity;
   		float CostPrice;
   	
   	public:
   		Fruit(float cost);
	   	float getCostPrice()
	   	{
	   		return CostPrice;
		}	
};

Fruit::Fruit(float cost):CostPrice(cost){ }

/*
Fruit::Fruit(float cost)
{
	CostPrice=cost;
}
*/

int main()
{
	Fruit strawberry(5.1); 
	cout<<"the strawberry's default costprice is "<<strawberry.CostPrice;
	cout<<endl;
	strawberry.CostPrice=5.3;
	cout<<"the strawberry's current costprice is "<<strawberry.getCostPrice();
	
	return 0;

}

3.3 拷贝构造函数

3.3.1 拷贝构造函数是一种特殊的构造函数,它在创建对象时,使用另一个同类的对象来初始化新创建的对象使用场景:类带有指针变量并有动态内存分配(malloc new)。如果类中没有定义拷贝构造函数,编译器会自行定义一个
3.3.2 构造函数的基本形态

classname (const classname &obj) //obj是一个对象引用,用于初始化另一个对象
{
   // 构造函数的主体
}

3.3.3 编码示例

#include <iostream>
using namespace std;
 
class Fruit
{
   public:
      float getCost()
      {
      	return *ptrCost;
	  }
	  
      Fruit( float cost )   //Construct Func. 
      {
      	
      	cout << "Construct Func. works..." << endl; 
		ptrCost = new float; //for pointer, memory allocation is first than use.
		*ptrCost = cost;
	  }
	  
      Fruit( const Fruit &obj)     // Copy Construct Func. 
      {
      	cout << "Copy Construct Func. works..." << endl;
      	ptrCost=new float;
      	*ptrCost = *obj.ptrCost; //copy value 
	  }
      
	  ~Fruit()                // deconstruct Func.
	  {
	  	cout << "Deconstruct Func. works and free memory." << endl; 
	  	delete ptrCost;
	  }
 
   private:
      float *ptrCost;
};

int main()
{
   Fruit Lemon(23.2);
   cout<<"Lemon is ready!"<<endl;
   Fruit Grape=Lemon; 
   cout<<"Grape is ready"<<endl;
 
   cout<<"Lemon's cost is: "<<Lemon.getCost()<<endl;
   cout<<"Grape's cost is: "<<Grape.getCost()<<endl;
 
   return 0;
}

在这里插入图片描述

3.4 析构函数

析构函数是类的一种特殊成员函数,在每次删除所创建对象时执行。析构函数的名称与类的名称完全相同,但需要在前面加波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(如关闭文件、释放内存)前释放资源

#include <iostream>
using namespace std;
 
class Fruit 
{
	public:
		string name;
   		int quatity;
   		float CostPrice;
   	
   	public:
   		Fruit()
   		{ cout<<"Object has been created."<<endl; }
   		~Fruit() //析构函数
   		{ cout<<"Object has been deleted."<<endl; }
	   	float getCostPrice()
	   	{
	   		return CostPrice;
		}	
};

int main()
{
	Fruit strawberry; 
	
	return 0;

}

3.5 友元函数 friend

友元可以是函数、也可以是类,其关键字是friend,友元可以访问类的private成员

#include <iostream>
using namespace std;
 
class Fruit
{
	public:
		friend void printFruit(Fruit fruit); //friend Func.
		Fruit(int qua,float p):quantity(qua),price(p){}; //使用初始化列表来初始化字段
	
	private:
		int quantity;
		float price;
};


void printFruit(Fruit fruit)
{
	cout<<fruit.quantity<<' '<<fruit.price; //friend Func. can access Pirate!
	cout<<endl;
}


int main()
{
	Fruit tomato(100,3.1);
	printFruit(tomato);
	return 0;
}

3.6 内联函数 inline

3.6.1 引入内联函数,是为了提升程序中函数调用的效率,本质是通过空间换时间,所以内联函数一般都是不超过10行的小函数
3.6.2 内联函数的关键字是 inline,在使用内联函数时要注意:
(1) 在内联函数中不允许使用循环语句和开关语句(break)
(2) 内联函数的定义必须出现在内联函数第一次调用之前
(3) 类的成员函数都是内联函数

#include <iostream>
using namespace std;
 
inline int min(int a,int b) //
{
	return (a<b)?a:b;
}

int main()
{
	cout<<"min(2,3):"<<min(2,3)<<endl;
	cout<<"min(2,1):"<<min(2,1)<<endl;
	
	return 0;
}

3.7 this指针

3.7.1 在成员函数内部,this可以用来指向调用对象
3.72 通俗的解释:当你进入一个房子后,你可以看到房子内的桌子、椅子、地板,但是你看不到房子的全貌。对于类来说,你可以看到成员函数、成员变量,但你看不到实例本身,使用this可以让我们看到这个实例本身。个人理解:类就好比这座房子,this就好比一把钥匙,通过钥匙来打开了这座房子的门,那么里面的东西就随意你使用
3.7.3 友元函数没有 this 指针,因为友元不是类的成员,只有成员函数才有 this 指针

#include <iostream>
using namespace std;
 
class Fruit
{
	public:
		float price;
		Fruit(int p):price(p){
		};
		bool compare(Fruit fruit)
		{
			return this->price > fruit.price;
		}	
};

int main()
{
	Fruit grape(2.23), orange(2.27);
	cout<<"Orange is more expensive than grape: ";
	if(grape.compare(orange))
		cout<<"true."<<endl;
	else
		cout<<"false."<<endl;
	
	return 0;
}

3.8 赋值运算符重载函数 operator=

3.8.1 operator= 就是对赋值运算符 = 进行重载,在部分情况下,类对象进行析构时,会重复释放一块内存,从而导致程序崩溃,这种情况下,就需要重载赋值运算符 = 了
3.8.2 这里的部分情况,我还没有完全理解,就示例中展示的情况是:类对象向类对象赋值 且 构造函数里面涉及指针操控,后面遇到了再研究吧
3.8.3 一般形式如下

String& operator=(const String &a_b) {}
	String B;
	B = A;

3.8.4 示例

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

class String
{
public:
	String()
	{
	}

	String(const char* ptrInputStr) //不加const会引起warning,why?
	{
		ptrStr = new char[strlen(ptrInputStr) + 1];
		strcpy_s(ptrStr, strlen(ptrInputStr) + 1, ptrInputStr);
	}

	~String()
	{
		delete ptrStr;
	}
	
	String& operator=(const String &a_b) //该程序如果没有 赋值运算符 = 重载函数,在vs2019下会触发程序崩溃
	{
		cout<<"operator= is work."<<endl;
		// 避免自赋值
		if (this != &a_b)
		{
			// 避免内存泄露
			if (ptrStr != NULL)
			{
				delete ptrStr;
				ptrStr = NULL;
			}

			ptrStr = new char[strlen(a_b.ptrStr)+1];
			strcpy_s(ptrStr, strlen(a_b.ptrStr) + 1,a_b.ptrStr);
		}

		return *this;
	} 

public:
	char* ptrStr;
};

int main()
{
	String A("keep coding");
	cout << A.ptrStr << endl;

	String B;
	B = A;

	cout << B.ptrStr << endl;

	return 0;
}

3.8.3 参考资料 C++中的赋值运算符重载函数(operator=)作者:liitdar

3.9 类的静态成员

3.8.1 可以通过static 关键字将类成员定义为静态的。对于静态成员,无论创建多少个类的对象,静态成员都只有一个副本,静态成员在类的所有对象中是共享的
3.8.2 静态成员变量的初始化不能放在类的定义中,但是可以在类的外部进行初始化。如果不存在初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。静态成员变量示例

#include <iostream>
using namespace std;

class Fruit
{
	public:
		static int count;
		Fruit()
		{
			count++;
		}
};

int Fruit::count=0;

int main()
{
	cout<<"Fruit's count is:"<<Fruit::count<<endl;
	Fruit apple;
	cout<<"Fruit's count is:"<<Fruit::count<<endl;
	Fruit banana;
	cout<<"Fruit's count is:"<<banana.count<<endl;
	Fruit orange;
	cout<<"Fruit's count is:"<<apple.count<<endl;
	
	return 0;
}

3.8.5 静态成员函数没有this指针,只能访问静态成员。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。示例如下:

#include <iostream>
using namespace std;

class Fruit
{
	public:
		static int count;
		Fruit()
		{
			count++;
		}
		static statistics() //静态成员函数
		{
			return count;
		}
};

int Fruit::count=0;

int main()
{
	cout<<"Fruit's count is:"<<Fruit::statistics()<<endl;
	Fruit apple;
	cout<<"Fruit's count is:"<<Fruit::statistics()<<endl;
	Fruit banana;
	cout<<"Fruit's count is:"<<Fruit::statistics()<<endl;
	Fruit orange;
	cout<<"Fruit's count is:"<<Fruit::statistics()<<endl;
	
	return 0;
}

二、继承

继承分为public, private, protected三种方式

1. public继承

#include <iostream>
using namespace std;
 
class Fruit 
{ 	
	public:
		float publicCost;
	protected:
		float protectedCost;
	private:
		float privateCost; 
};

class HardShellFruit:public Fruit //HardShellFruit是 public继承 Fruit的子类 
{
	public:
		void PrintPrice()
		{
			cout<<publicCost;
			cout<<protectedCost; //基类的protected成员,在派生类中仍是protected,因此可以被派生类访问
			//cout<<privateCost; 基类的private成员不能被派生类访问 
		} 
}; 

int main( )
{
	HardShellFruit mangosteen; 
	
	mangosteen.publicCost=20.1;
	//mangosteen.protectedCost; 无法访问
	// mangosteen.privateCost; 无法访问 
	return 0;
}

2. protected继承

#include <iostream>
using namespace std;
 
class Fruit 
{ 	
	public:
		float publicCost;
	protected:
		float protectedCost;
	private:
		float privateCost; 
};

class HardShellFruit:protected Fruit //HardShellFruit是继承自Fruit的子类 
{
	public:
		void PrintPrice()
		{
			cout<<publicCost; //protected继承,基类public成员变为 protected成员,可以在子类访问 
			cout<<protectedCost; //protected继承,基类protected成员还是protected成员,可以在子类访问
			//cout<<privateCost; 基类的private成员不能被派生类访问
		} 
}; 

int main( )
{
	HardShellFruit mangosteen; 
	
	//mangosteen.publicCost=20.1;  protected继承,类public成员变为 protected成员,无法在类外访问 
	//mangosteen.protectedCost; 无法访问
	// mangosteen.privateCost; 无法访问 
	return 0;
}

3. private继承

private也是默认继承

#include <iostream>
using namespace std;
 
class Fruit 
{ 	
	public:
		float publicCost;
	protected:
		float protectedCost;
	private:
		float privateCost; 
};

class HardShellFruit:private Fruit //HardShellFruit是继承自Fruit的子类 
{
	public:
		void PrintPrice()
		{
			cout<<publicCost; //private继承,基类public成员变为 private成员,可以在子类访问 
			cout<<protectedCost; //private继承,基类protected成员变为 private成员,可以在子类访问 
			//cout<<privateCost; 基类的private成员不能被派生类访问
		} 
}; 

int main( )
{
	HardShellFruit mangosteen; 
	
	//mangosteen.publicCost=20.1;  private继承,类public成员变为 private成员,无法在类外访问 
	//mangosteen.protectedCost; 无法访问
	// mangosteen.privateCost; 无法访问 
	return 0;
}

三、其他

1. new/delete 与 malloc/free

  1. new和malloc都是告诉计算机开辟一段新的存储空间。大部分场景下new和malloc可以通用
  2. 他们之间的区别包括:
    2.1 malloc与free是c++/c语言的标准函数,new/delete是C++的运算符
    2.2 都可用于申请动态内存和释放内存。new/delete比malloc/free更加智能,因为new和delete在对象创建的时候自动执行构造函数,对象消亡之前会自动执行析构函数 //目前不是很理解
    2.3 new/delete的功能完全覆盖了malloc和free,但C++不能把malloc/free淘汰,因为C++程序会调用C函数,而C只能用malloc/free管理动态内存
    2.4 new返回指定类型的指针,并且可以自动计算出所需要的大小。malloc必须用户指定大小,并且默然返回类型为void*,必须强行转换为实际类型的指针。请看示例
#include <stdlib.h>
#include <iostream>
using namespace std;

#define max 10

int main()
{
	int i;
	int *p; //指针使用前先申请空间
	p=(int*)malloc(sizeof(int)) ;
	*p=1;
	
	int *p_new;
	p_new=new int;
	*p_new=2;
	
	cout<<"p="<<*p<<" p_new="<<*p_new<<endl;
	
	free(p) ;
	delete p_new; 
	
	int *a;
	a=(int*)malloc(sizeof(int)*max);
	
	int *b;
	b=new int[max];
	
	for(int i=0;i<max;i++)
	{
		a[i]=i+10;
		b[i]=i+100;
	}	
		
	cout<<"start print:"<<endl;
	i=max;
	while(i--)
	{
		cout<<a[i]<<"---"<<b[i]<<endl;
	}
	
	free(a);
	delete b;
	
	return 0;
}


2. reference 引用

  1. reference 引用:引用是已存在变量的另一个名字, 对应的符号是 & (指针的符号是 * )。引用和指针的区别是:
    (1)不存在空引用,引用必须连接到一块合法的内存
    (2)一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向另一个对象
    (3)引用必须在创建时被初始化;指针可以在任何时间被初始化
  2. 进一步解释
    (1)指针 B 指向 A:B 借用了 A 的值,但是B的内存地址和A不同。引用 B 引用 A:B的内存地址和值 与A 完全相同,A和B任意一个发生改变, A和B同时改变
    (2)套用火影设定:指针更像是普通的分身法(虚拟分身),即使是分身破灭,对自身也没有影响;引用更像是影分身(实体分身),影分身发生了变化,本身也会受到影响
    (3)你被朋友起了绰号"派大星",这个绰号就相当于你名字的一个引用,在朋友看来,无论叫哪个都是在叫你
  3. 示例代码
#include <iostream>
using namespace std;
 
int main ()
{
   int i;
   int &reference=i;
   
   i=3;
   cout<<i<<' '<<reference<<endl; //print:3 3
   
   reference=4;
   cout<<i<<' '<<reference<<endl; //print:4 4
   
   return 0;
}
  1. 先进行下了解,后面在应用中再继续补充

3. vector基础使用

  1. vector是STL中的一种数据结构(也称为容器),相当于数组的加强版,使用时需要 #include 。STL Standard Template Library 标准模板库
  2. 例题:给定n个字符串,对n个字符串按照字典序排列
#include <algorithm>  //sort
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int input;
	string str;
	vector<string> vs; //vector的存储对象是string 
	
	cin>>input; //输入待排序的元素个数 
	
	while(input--)
	{
		cin>>str;
		vs.push_back(str); // push_back():向vs中添加元素 
	}
	
	sort(vs.begin(),vs.end()); //begin() 首元素, vs.end() 尾元素 
	
	vector<string>::iterator vit; //定义迭代器 
	
	cout<<"Print result:"<<endl;
	
	for(vit=vs.begin();vit!=vs.end();vit++)
		cout<<*vit<<endl;
	
	return 0;
} 

4. const

const: 定义一个常量,程序执行期间不能改变。把常量定义为大写字母形式,是一个很好的编程习惯

5. size_t

size_t 同 int 一样,都可以进行变量定义: int a; size_t a; 都代表定义了一个整形变量a. 但是int固定为4byte,而size_t的大小与OS有关、因此移植性更好

参考资料

RUNOOB.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值