@[TOC]类

类相关

//Stock.h
#pragma once
class Stock {
private:
	double total_val;
	void fun();// 
public:
	double GetVal();
	void set_val();
	void show();//cout显示
};
//内联,定义在类声明中的函数自动为内联函数,若也可以在声明函数之外定义内联 + inline
//也可以放在.cpp文件中,通常将类定义放在头文件中。或者将实现放在代码声明的地方
inline void Stock::set_val()
{
	total_val = 3;
}
//Stock.cpp
#include"Stock.h"
#include<iostream>
using namespace std;
void Stock::fun()
{
	std::cout << "private::fun" << std::endl;
}

double Stock::GetVal()
{
	return total_val;
}
void Stock::show()
{
	//使用定点表示法
	ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
	//显示三位小数
	std::streamsize  prec = cout.precision(3);

	打印代码
	//恢复之前的
	cout.setf(orig, ios_base::floatfield);
	cout.precision(prec);
}
#include <iostream>
#include"Stock.h"
int main()
{
    std::cout << "Hello World!\n";
    Stock st;
    st.GetVal();  //只能通过公有有成员函数(和友元函数)访问对象的私有成员。
   //  st.fun();不行,私有成员函数,只有类的公有成员函数才可以使用
}

构造函数

定义对象时调用构造,构造负责创建对象
显示调用构造 Stock st = Stock(5);
隐式调用构造 Stock st (5); Stock * st = new Stock(5,7);
默认构造 Stock st; 调用默认的构造函数
通常,若没有定义任何构造函数,C++将提供一个默认的构造,不做任何处理。必须提供一个默认构造。两种方式:

  1. Stock(){ 给每个参数赋值}
  2. Stock(int b= 5,int c =4){} // 提供默认的值
    默认构造函数只能有一个。
    在设计类时,通常应该提供对所有类成员做隐式初始化的默认构造函数(方法1)

析构函数

对象过期时自动调用析构。
何时调用析构函数由编译器决定。
如果创建的是静态存储类对象,则其析构函数将在程序结束时自动被调用。
如果创建的是自动存储类类对象,则析构将在程序执行完代码块时自动被调用。
若是new的,则它将驻留在栈内存中或自由存储区中,使用delete来释放内存时,析构将自动调用。
必须有一个析构,若没有,编译器将隐式的提供一个默认析构。

注意:Stock st;
st = Stock(5);//构造创建一个新的临时的对象,然后将值赋给st。
C++11 支持初始化列表初始化: Stock st{5} ;

const 成员函数

const Stock st; st.show(); //不行。除非将show函数声明为 void show()const;

this指针

this是对象的地址,*this 对象本身

类作用域

在类中定义的名称的作用域 为整个类(数据成员和成员函数),作用域为整个类的名称在该类中已知,类外不可知。意味着不能从外部方位类的成员,只能通过类对象访问。

作用域为类的常量

class A{
private: const int Months = 12;
}
不行,因为声明类只是描述了对象的形式,并没有创建对象。因此创建对象前,没有用于存储值得空间

  1. 声明一个枚举
    class A{
    private:
    enum{ Months = 12};
    }
    这种方式声明枚举并不会创建类数据成员,即所有对象中不包含枚举,另外,Months只是一个符号名称,在作用域为整个类的代码中遇到它,编译器都将用12替代。(类似的ios_base类的ios_base::fixed等标识符)

  2. 使用关键static
    class A{
    private:
    static const int Months = 12;
    double int【12】;
    }
    该常量与其它静态变量存储在一起,而不是存储在对象中。因此只有一个Months常量,被所有对象共享。

作用域内枚举(C++11)

传统枚举可能发生冲突。
enum egg {Small,Big,Middle};
enum T_shirt { Small, Big, Middle };
两个位于相同的作用域,编译通不过。

  1. C++11允许
    enum class egg {Small,Big,Middle}; //class 可用struct替代
    enum class T_shirt { Small, Big, Middle };
    egg s = egg::Small;
  2. C++11提高了作用域内枚举的类型安全。在某些情况下,常规枚举将自动转为整型,如将其赋给int变量或用于比较表达式时,但作用域内枚举不能隐式的转为整型。
    enum egg {Small,Big,Middle};
    enum class T_shirt { Small, Big, Middle };
    egg one = Small;
    T_shirt t = T_shirt::Big;
    int a = one; //常规枚举自动转为整型
    // int b = t;//不允许。若非要,可以强转
    int b =(int)T_shirt::Big;
    int c = int(t);
int d = 5;
if(a<5)
    std::cout << "Hello World!\n";
if(b<5)  // 不允许
3. 默认情况下,C++11作用域内枚举的底层类型为int。可用以下修改
enum class :short egg {Small,Big,Middle};   :short将底层类型指定为short

抽象数据类型 (ADT)

通常定义类来表示更通用的概念
栈的实现。

//Stack.h
#pragma once
typedef double Item;  //可根据需要重定义为不同的
class Stack {
private:
	enum {MAX = 10};
	Item m_itemsArray[MAX];
	int m_top;
public:
	Stack();  //确保所有栈被创建时为空
	bool isEmpty() const;
	bool isFull() const;
	bool push(const Item& item);  //确保栈顶被正确处理,oop可靠性的原因
	bool pop(Item& item);
};
//Stack.cpp
#include "Stack.h"

Stack::Stack()
{
	m_top = 0;
	m_itemsArray[MAX] = { 0 };
}

bool Stack::isEmpty() const
{
	return m_top == 0;
}

bool Stack::isFull() const
{
	return m_top == MAX;
}

bool Stack::push(const Item& item)
{
	if (m_top < MAX)
	{
		m_itemsArray[m_top++] = item;
		return true;
	}
	else 
	{
		return false;
	}	
}

bool Stack::pop(Item& item)
{
	if (m_top > 0)
	{
		item = m_itemsArray[--m_top];
		return true;
	}
	else
	{
		return false;
	}
}

#include <iostream>
#include"Stack.h"
#include<cctype>
using namespace std;
int main()
{
	Stack sk;
	char ch;
	double po;
	cout << "A to add , P to process ,  Q quit\n";
	while (cin >> ch && toupper(ch) != 'Q')
	{
		while (cin.get()!='\n')
		{
			continue;
		}
		if (!isalpha(ch)) 
		{
			cout << '\a' << endl;
			continue;
		}
		switch (ch)
		{
		case 'A':
		case 'a':
			cout << "enter a double to add \n";
			cin >> po;
			if (sk.isFull())
			{
				cout << "full" <<endl;
			}
			else
			{
				sk.push(po);
				cout << "Po # " << po << endl;
			}
			break;
		case 'P':
		case 'p':
			if (sk.isEmpty())
			{
				cout << "Empty" << endl;
			}
			else
			{
				sk.pop(po);
				cout << "Po # " << po <<"poped "<< endl;
			}
			break;
		}
		cout << "A to add , P to process ,  Q quit\n";
	}
	cout << "bye\n";
}

使用类

运算符重载

重载示例(Time类)

//Time.h
#pragma once
class Time {
private:
	int hours;
	int minutes;
public:
	Time(); //默认构造函数
	Time(int h,int m = 0);
	void addMin(int m);
	void addHour(int h);
	void reSet();
	Time Sum(const Time& t) const;
	void Show()const;	
	//重载运算符
	Time operator+(const Time& t)const;
	Time operator-(const Time& t)const;
	Time operator*(double n)const;
};
//Time.cpp
#include"Time.h"
#include<iostream>
Time::Time()
{
	hours = minutes = 0;
}
//声明加默认的m=0了 ,定义不需要,否则提示重定义
Time::Time(int h, int m) 
{
	hours = h;
	minutes = m;
}
void Time::addMin(int m) 
{
	minutes += m;
	hours += minutes / 60;     //此处巧妙
	minutes %= 60;
}
void Time::addHour(int h)
{
	hours += h;
}
void Time::reSet()
{
	hours = minutes = 0;
}
//传递引用 速度更快,返回函数内的变量,将创建临时变量
Time Time::Sum(const Time& t) const
{
	Time sum;
	sum.minutes = this->minutes + t.minutes;
	sum.hours = hours + t.hours + sum.minutes / 60;
	sum.minutes = sum.minutes % 60;
	return sum;   //此处不能返回引用,因为sum为局部变量
	//注意:不要返回指向局部变量或临时变量的引用。
	//函数执行完后,局部变量和临时变量将消失,引用将指向不存在的数据
}
void Time::Show()const
{
	//就这一处 使用cout ,使用std::cout比导入整个名称空间更好
	std::cout << hours << " : " << minutes<<"\n";
}

Time Time::operator+(const Time& t) const
{
	//与sum函数相似,只是改变名称了
	Time sum;
	sum.minutes = this->minutes + t.minutes;
	sum.hours = hours + t.hours + sum.minutes / 60;
	sum.minutes = sum.minutes % 60;
	return sum;  
}
Time Time::operator-(const Time& t) const
{
	Time tmp;
	int t1, t2;
	t1 = t.minutes + t.hours * 60;
	t2 = minutes + hours * 60;
	tmp.hours = (t2 - t1) / 60;
	tmp.minutes = (t2 - t1) % 60;
	return tmp;
}

Time Time::operator*(double n) const
{
	Time tmp;
	long ti = hours * n * 60 + minutes * 60;
	tmp.hours = ti / 60;
	tmp.minutes = ti % 60;
	return tmp;
}

#include <iostream>
#include"Time.h"
int main()
{
    std::cout << "Hello World!\n";
    Time t1;
    Time t2(5, 6);
    Time total = t1.Sum(t2);
    total.Show();
    //运算符调用 两种都可以
    total = t1.operator+(t2); //t1为调用对象,t2为传入的参数值
    total = t1 + t2;
    Time a, b, c;
    total = a + b + c;
    //被解释为(+从左到右结合)
    total = a.operator+(b + c);
    total = a.operator+(b.operator+(c));
}

重载限制

  1. 重载后的运算符,至少有一个操作数是用户定义的类型,防止用户为标准类型重载运算符。
  2. 使用运算符时不能违反原来的语法规则,也不能修改运算符优先级
  3. 不能创建新的运算符 ,不能用**表示求平方
  4. 不能重载下面的运算符
    sizeof , : ,* ,::, ? : ,typeid, const_cast , dynamic_cast ,reinterpret_cast ,static_cast;
  5. 下表的大多数运算符都可以通过成员或非成员函数进行重载,但下面的只能通过成员函数进行重载
    = () 【】 ->
    在这里插入图片描述
    在这里插入图片描述

友元

友元有友元函数,友元类,友元成员函数

  1. 为何使用友员
    上述Time类中,若A B 是Time对象
    A = B*2;=>A = B.operator (2);
    但 A = 2
    B; 将不能正确转换,左侧操作数是调用对象,但2不是对象
    大多数运算符都可以通过成员函数或非成员函数来重载
    特殊的非成员函数,友元函数可以访问私有成员
声明: friend Time operator*(double n,const Time &t) ;
//此处不加friend
Time operator*(double n, const Time& t)
{
	Time tmp;
	long ti = t.hours * n * 60 + t.minutes * 60;
	tmp.hours = ti / 60;
	tmp.minutes = ti % 60;
	return tmp;
	///也可以
	return t*n;
}

提示:如果要为类重载运算符,并将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序。
将友元看作类的扩展接口的组成部分。

重载<<

  1. 版本1
friend void operator<<(std::ostream& os, const Time& t)
	{
		os << t.hours << " : " << t.minutes << std::endl;
	}
	//缺点不能够  cout<<t<<"eee"<<endl; 既连续赋值
  1. 版本2
	friend std::ostream & operator<<(std::ostream& os, const Time& t)
	{
		os << t.hours << " : " << t.minutes << std::endl;
		return os;
	}

友元还是成员

成员:Time operator+(const Time& t)const; 一个操作数通过this,一个通过传参
友元 : friend Time operator+(const Time& t,const Time& t2)const; 两个都传参
注意:两个不能同时出现否则会二义性。有时根据类设计,使用非成员函数版本可能会好点(尤其是为类定义类型转换时)

再谈重载,一个矢量类(Vector类)

  1. vector 类
vect.cpp
#include"vect.h"
#include<cmath>
using std::sqrt;
using std::sin;
using std::cos;
using std::atan;
using std::atan2;
using std::cout;

namespace VECTOR   //利用开放性
{
	const double Rad_to_deg = 45.0 / atan(1.0);  //atan反正切   tanPAI/2 = 1
	void Vector::set_mag()
	{
		mag = sqrt(x * x + y * y);
	}
	void Vector::set_ang()
	{
		if (x == 0.0 && y == 0.0)
		{
			ang = 0;
		}
		else
		{
			ang = atan2(y , x);
		}
	}
	void Vector::set_x()
	{
		x = mag * cos(ang);
	}
	void Vector::set_y()
	{
		x = mag * sin(ang);
	}
	Vector::Vector()
	{
		x = y = mag = ang = 0;
		mode = RECT;
	}
	//Vector v(11,2,2) 不能通过编译,2不能隐式的转为枚举类型
	// Vector v(11, 2, VECTOR::Vector::Mode(2));  //发出警告,无法识别
	Vector::Vector(double n1, double n2, Mode form/* = RECT*/)
	{
		mode = form;
		if (form == RECT)
		{
			x = n1;
			y = n2;
			set_ang();
			set_mag();
		}
		else if (form == POL)
		{
			mag = n1;
			ang = n2 / Rad_to_deg;
		}
		else
		{
			cout << "error argument ,will set 0 \n";
			x = y = mag = ang = 0;
			mode = RECT;
		}
	}
	//该方法必不可少,若v = Vector(11,22);则会创建临时对象
	void Vector::reset(double n1, double n2, Mode form/* = RECT*/)
	{
		mode = form;
		if (form == RECT)
		{
			x = n1;
			y = n2;
			set_ang();
			set_mag();
		}
		else if (form == POL)
		{
			mag = n1;
			ang = n2 / Rad_to_deg;
			set_x();
			set_y();
		}
		else
		{
			cout << "error argument ,will set 0 \n";
			x = y = mag = ang = 0;
			mode = RECT;
		}
	}
	Vector::~Vector()
	{}
	void Vector::polar_mode()
	{
		mode = POL;
	}
	void Vector::rect_mode()
	{
		mode = RECT;
	}
	//重载  使用构造进行+ ,创建新对象,返回该对象的副本,确保新对象是根据构造的规则指定的
	Vector Vector::operator+(const Vector& b) const
	{
		return Vector(x + b.x, y + b.y);
	}
	//重载 - 只要特征标不同,就可以重载多个
	Vector Vector::operator-(const Vector& b) const
	{
		return Vector(x - b.x, y - b.y);
	}
	Vector Vector::operator*(double n) const
	{
		return Vector(n*x, n*y);
	}
	//重载 - 
	Vector Vector::operator-() const
	{
		return Vector(-x,-y);
	}
	//friend
	Vector operator*(double n, const Vector& b)
	{
		return Vector(b * n);
	}
	std::ostream& operator <<(std::ostream& os, const Vector& b)
	{
		//友元 在VECTOR内,不在Vector ,因此需要Vector::RECT
		if (b.mode == Vector::RECT)
		{
			os << "(x,y) " << b.x << "  : " << b.y<<"\n";
		}
		else if(b.mode == Vector::POL)
		{
			os << "(m,a) " << b.mag << "  : " << b.ang*Rad_to_deg << "\n";
		}
		else
		{
			os << "Vector mode is vailid";
		}
		return os;
	}
}
//vect.h
#pragma once
#include<iostream>

namespace VECTOR 
{
	class Vector {
	public:
		enum Mode{RECT,POL};
	private:
		//对象可以只存储x y  其它的通过计算得到,若经常需要两种,则本例好。
		double x, y;
		double mag, ang;
		Mode mode;
		void set_mag();
		void set_ang();
		void set_x();
		void set_y();
	public:
		Vector();
		Vector(double n1,double n2,Mode form = RECT);
		void reset(double n1, double n2, Mode form = RECT);
		~Vector();
		double xval() const { return x; }
		double yval() const { return y; }
		double magval() const { return mag; }
		double angval() const { return ang; }
		void polar_mode();
		void rect_mode();
		//重载
		Vector operator+(const Vector& b) const;
		Vector operator-(const Vector& b) const;
		Vector operator*(double n) const;
		Vector operator-() const;
		//friend
		friend Vector operator*(double n, const Vector& b);
		friend std::ostream& operator <<(std::ostream& os, const Vector& b);
	};
}

模拟随机漫步

#include <iostream>
#include<cstdlib>  //rand()  srand()
#include<ctime>   //time()  
#include"vect.h"
using namespace std;
int main()
{
    using  VECTOR::Vector;
    srand(time(0));   
    //time(0) 返回当前时间,通常为从某一个日期开始的秒数
    //rand()伪随机数,调用10次  产生同样的随机数(用一个初始种子值获得随机值,返回的随机值将作为下一次调用的种子)
    //srand()覆盖默认的种子。
    double direct;
    Vector step;
    Vector result(0.0, 0.0);
    unsigned long steps = 0;;
    double target;
    double dstep;
    cout << "Enter target distance (q to quit): ";
    while (cin>>target)
    {
        cout << "Enter step length: ";
        if (!(cin >> dstep))
            break;
        while (result.magval() < target)
        {
            direct = rand() % 360;
            step.reset(dstep, direct, Vector::POL);
            result = result + step;
            steps++;
        }
        cout << "after :" << steps << "  steps ,the subject has following location \n";
        cout << result << endl;
        result.polar_mode();
        cout << "or \n" << result << endl;
        cout << "average outward distance per step = " << result.magval() / steps << endl;
        steps = 0;
        result.reset(0.0, 0.0);
        cout << "Enter target distance (q to quit): ";
    }
    cout << "byte\n";
    cin.clear();
    while (cin.get() != '\n')
        continue;
    return 0;
    
}

类的自动转换和强制类型转换

  1. C++处理内置类型的转换
    long a = 8; 8->long double er = 5; 5->double int a = 3.3 3.3->int;(编译器自动进行的,会降低精度)
    不自动转换不兼容的类型。 下面语句非法
    int *p = 10;
    但在无法进行自动转换时,可以强制类型转换 int *p = (int *) 10 ;

其它类型转类类型

Stonewt(double lbs); 若有这样的构造,则可以将double转为Stonewt的对象。
即 Stonewt ts; ts = 12.6; 程序将使用上述构造函数,构建一个临时对象,并将12.6作为初始值,随后逐成员复制到ts中,这以过程称为隐式转换,因为其自动进行。Stonewt(double lbs,int a = 3); 这样也可以
explict Stonewt(double lbs); 关闭自动转换的特性,但仍然允许显示转换
ts = 12.6 // 不行 但 ts = Stonewt (12.3) 或者ts = (Stonewt) 12.3这样可以
2. 何时使用Stonewt(double lbs);,若加了关键字explict,则这能用于显示强制类型转换,否则可以下面的隐式类型转换
(1)将Stonewt初始化为double值时
(2)将double赋给Stonewt对象
(3)double传递给接收Stonewt参数的函数
(4)在上述任何一种情况下,使用可转换为double类型的内置类型
Stonewt bs (50);
bs = 60; 上述都先将int->double ->Stonewt;
仅当不存在二义性的时候才可以,若还定义了Stonewt(long lbs);则不行
display( Stonewt & bs);若display(5);则编译器先查找Stonewt(int)的构造,没有,则查找可以将int转为其他的内置类型Stonewt(double lbs),即int->double ->Stonewt;

转换函数

转换函数时用户定义的强制类型转换
Stonewt st(12.6);
double s = double(st); //double s = (double)st;
也可以让编译器决定如何做
double t = st;编译器将查找相关的转换函数,若找不到,报错。
1. 转换函数: operator int() const ; operator int();
注意:转换函数必须是类方法,不能指定返回类型,不能有参数
Stonewt st(12.6);
double s = st;
cout<<st<<endl; 编译错误,若同时指定了int和double的转换则不行,若只有一个则可以。
cout<<(int)st<<endl; //cout<<(double)st<<endl;
2. 一般 explic operator int() const ; 这样只能强转,关闭了隐式类型转换。
或者定义一个功能相同的非转换函数即可
int Stonewt_to_int (){return ss;}
总之C++为类提供了下面的类型的转换
只有一个参数的类的构造函数,将用于将类型与该参数相同的值转为类型,若无explict则自动调用,否则需要强转。
无explict会自动进行转换函数

转换函数和友元函数

为 类重载加法,可以使用成员函数或者友元函数(二选一)
Stonewt Stonewt::operator + (const Stonewt &st) const
{
double s = p + st.p;
Stonewt sum(s);
return sum;
}
或者
Stonewt operator + (const Stonewt &st,const Stonewt &st1) const
{
double s = st1.p + st.p;
Stonewt sum(s);
return sum;
}
若提供了Stonewt(double lbs); 则
Stonewt st(12.6);
double dd = 15.6;
Stonewt sum;
sum = st + dd;
sum = dd + st; //只有友元才可以这样
若同时定义了转换则会导致二义性,是double 加 还是类+;
经验:将加法定义为友元,可以让程序更容易适应自动类型转换,因为两个操作数都为函数参数。
注意 : double + Stonewt 两种方法
1 . 友元
operator + (const Stonewt &st,const Stonewt &st1) const

2 . 成员函数
Stonewt operator + (double x);
friend Stonewt operator + (double x,const Stonewt &st);
一种方法使程序简短,但依赖隐式转换,多次调用转换构造函数,增加内存开销。
一种程序长,但执行速度快。

类和动态内存分配

类中: static int num;
类外初始化 : int A::num = 0;
静态类成员无论初始化多少成员,所有成员共享一个静态成员。
静态类成员应该在实现文件中,否则包含头文件会有多个副本。
静态数据成员在类声明中声明,在包含该类方法的文件中初始化。但如果静态成员是整型或者枚举型const,则可以在声明中初始化。

特殊成员函数

若没有定义,C++默认生成构造,析构,复制,赋值,地址运算符,C++11新增移动构造函数,移动赋值运算符

复制构造函数

将一个对象复制到新创建的对象中
何时调用: String str(str1);
String str = str1;
String str = String(str1);
String *st = new String(str);// 初始化一个匿名对象,将该对象的地址给st。
中间两个可能会直接使用复制构造函数直接生成str,也可能,使用复制构造函数创建临时对象,然后将临时对象内容赋给str。
注意:每当程序生成了对象副本,编译器都将使用复制构造函数。函数按值传递对象或函数返回对象,都会调用。按值传递意味着创建原始对象的一个副本,编译器生成临时对象,也将使用。何时生成临时对象,因编译器而定,但按值传递和返回对象,一定会调用复制构造。
应该尽量使用按引用传递对象,可以节省调用复制函数的时间以及存储新对象的空间。
默认的复制构造执行浅拷贝,需要自定义一个。

赋值运算符

将已有的对象赋给另一个对象时,将使用重载的赋值运算符。
String str;
str = str1;
注意 String str = str1;并不一定会使用赋值运算符。可能是先用复制构造函数创建一个临时对象,然后通过赋值,将临时对象复制到新对象中。
初始化总是会调用复制构造函数,而使用=可能调用赋值运算符。

//string.h
#pragma once
#include<iostream>
class String {
private:
	char* str;
	int len;
	static int num_strings;
	static const int CINLIM = 80;
public:
	String(); //默认构造
	String(const char* s);
	String(const String & s);
	~String();
	String& operator=(const String& s);
	//c风格的字符串 --》string
	String& operator=(const char *s);
	int length()const { return len; }

	friend std::ostream& operator <<(std::ostream& os, const String& st);
	friend std::istream& operator >>(std::istream& is, String& st);
	//声明为友元,可以让c风格的字符串和String对象比较
	// "love" 将使用转换构造
	friend bool operator<(const String& s1, const String& s2);
	friend bool operator>(const String& s1, const String& s2);
	friend bool operator==(const String& s1, const String& s2);
	char& operator[](int i);
	const char& operator[](int i) const;
	static int HowMany();
};
//String.cpp
#include"String.h"
#pragma warning(disable:4996)  //防止禁用strcpy

#include<cstring>
using std::cin;
using std::cout;

int String::num_strings = 0; // 静态成员初始化
String::String() //默认构造
{
	/*len = 4;
	str = new char[4]; 
	strcpy(str, "C++");
	num_strings++;*/
	len = 0;
	str = new char[1];   // str = new char  与析构不兼容
	str[0] = '\0';        //可以简化为str = 0;
	num_strings++;
}
String::String(const char* s) 
{
	len = std::strlen(s);    //strlen 不包括\0
	str = new char[len + 1];
	strcpy(str, s);
	num_strings++;
}
String::String(const String& s)
{
	num_strings++;
	len = s.len;
	str = new char[len + 1];
	strcpy(str, s.str);
}
String::~String() 
{
	--num_strings;
	delete[] str;
}
String& String::operator=(const String& s)
{
	if (this == &s)
		return *this; //防止自己复制自己
	delete[] str;  //释放自己已经分配的内存
	// num_strings++;   赋值之间对象已经存在,无需++
	len = s.len;
	str = new char[len + 1];
	strcpy(str, s.str);

	return *this;
}
String& String::operator=(const char* s)
{
	delete[] str;  //释放自己已经分配的内存
	// num_strings++;   赋值之间对象已经存在,无需++
	len = std::strlen(s);
	str = new char[len + 1];
	strcpy(str, s);
	return *this;
}
std::ostream& operator <<(std::ostream& os, const String& st)
{
	os << st.str;
	return os;
}
std::istream& operator >>(std::istream& is, String& st) 
{
	char tmp[String::CINLIM];
	is.get(tmp, String::CINLIM);  //到达文件尾或空行,读取失败
	if (is)   
	{
		st = tmp;
	}
	while (is&&is.get()!='\n')    //丢弃多余的字符
	{
		continue;
	}
	return is;
}
bool operator<(const String& s1, const String& s2)
{
	if (std::strcmp(s1.str, s2.str) < 0)
		return true;
	else
		return false;
	//可以简化为 return (std::strcmp(s1.str, s2.str<0);
}
bool operator>(const String& s1, const String& s2)
{
	return s2 < s1;
}
bool operator==(const String& s1, const String& s2)
{
	return (std::strcmp(s1.str, s2.str) == 0);
}
char& String::operator[](int i)  //返回为char &   可以 str【i】 = ‘w’;
{
	return str[i];   //
}
//若只有上述定义,const String str; str[4]会出错
const char& String::operator[](int i) const  
{
	return str[i];   //
}
//静态成员函数,只能访问静态数据成员,len和str没法访问。不能使用this指针
//不能通过对象调用静态成员函数。只能通过int a = String::HowMany()
//定义时和friend一样 不加static
int String::HowMany()
{
	return num_strings;
}
int main()
{
	cout << "Enter your say :\n";
	String says[5];
	char tmp[20];
	for (int i = 0; i < 5; i++)
	{
		cout << i + 1 << " :";
		cin.get(tmp, 20);
		while (cin&&cin.get()!='\n')
		{
			continue;
		}
		if (!cin || tmp[0] == '\0')    //空行
			break;
		else
		{
			says[i] = tmp;
		}
	}

	int total = 5;
	if (total > 0)
	{
		cout << "here your says \n";
		for (int i = 0; i < 5; i++)
		{
			cout << i + 1 << " :" << says[i] <<endl;;
		}
		int shortest = 0;
		int first = 0;
		for (int i = 0; i < 5; i++)
		{
			if (says[i].length() < says[shortest].length())
				shortest = i;
			if (says[i] < says[first])
				first = i;
		}
		cout << "short : " << says[shortest] << endl;
		cout << "first : " << says[first] << endl;
		cout << "how many : " << String::HowMany();
	}
}

在构造中使用new注意事项

1.在构造中使用new初始化指针成员,则应在析构中使用delete
2.new和delete必须互相兼容。new delete new【】和delete【】
3.若有多个构造,则必须以相同的方式new,要么都带中括号,要么都不带,析构只有一个,为了与析构兼容。但可以在一个构造中初始化指针,而在另一构造中将其为nullptr。

有关返回对象的说明

成员函数或函数,返回对象时,有几种方式,返回指向对象的引用,返回指向对象的const引用或const对象。

  1. 返回指向const对象的引用
    const String & Max(const String &s1,const String &s2);
    返回对象调用复制构造函数,返回引用不是。因此返回指向const对象的引用效率更高,。其次引用指向的const对象在调用函数执行时始终存在。参数也必须匹配。
  2. 返回指向非const对象的引用
    重载运算符 = 和 << ,提高效率
  3. 返回对象
    如果被返回的对象是被调函数中的局部变量,则不应该按引用的方式返回它。重载的算术运算符属于这一类。
  4. 返回const对象
    如果方法或函数要返回局部对象,则应该返回对象,而不是指向对象的引用。如果方法或函数要返回一个没有公有复制构造函数的类(ostream类的对象),它必须返回一个指向这种对象的引用。有些方法和函数,如重载的赋值运算符可以返回对象,也可以返回指向对象的引用,这种情况下,首选引用,因为其效率更高。

使用指向对象的指针

String *str = new String; //调用默认构造函数
String *str = new String(value ); //调用String(value );构造函数
在这里插入图片描述

指针和对象小结

在这里插入图片描述
在这里插入图片描述

定位new运算符

char * buff = new char [512];
String * p1 = new(buff)String (“sssasf”);
String * p2 = new(buff+sizeof(String ))String (“sssasf”);
p2->~String();
p1->~String();
delete [] buff;

队列模拟

成员初始化列表语法

  1. 只适用于构造函数
  2. 必须用这种格式来初始化非静态const 数据成员(C++11之前是这样)
  3. 必须用这种格式来初始化引用数据成员
//Queue.h
#pragma once
#include<cstdlib>
#include<iostream>
class Customer {
private:
	long arrive;
	int processtime;
public:
	Customer() { arrive = processtime = 0; }
	void set(long when)
	{
		processtime = std::rand() % 3 + 1;
		arrive = when;
	}
	long when()const
	{
		return arrive;
	}
	int ptime() const 
	{
		return processtime;
	}
};
typedef Customer Item;
class Queue {
private:
	struct Node
	{
		Item item;
		struct Node* next;
	};
	enum { Q_SIZE = 10 };
	Node* front;
	Node* rear;
	int items;
	const int qsize;//const int qsize = Q_SIZE c++11 新增
	//声明私有 有两个作用,避免了本来将自动生成默认的方法的定义;方法私有不能直接使用
	//因为赋值,复制一个新的链表,都指向一个开头和结尾,没有实际作用
	Queue(const Queue& q) :qsize(0) {}   //const成员初始化,只能在初始化列表中
	Queue& operator = (const Queue& q) { return *this; }
public:
	Queue(int qs = Q_SIZE);
	~Queue();
	bool isEmpty()const;
	bool isFull() const;
	int queueCount() const ;
	bool enqueue(const Item& item);
	bool dequeue(Item& item);
};
#include"Queue.h"
Queue::Queue(int qs) : qsize(qs)
{
	front = rear = nullptr;
	items = 0;
}
Queue::~Queue()
{
	Node* tmp;
	while (front != nullptr)
	{
		tmp = front;
		front = front->next;
		delete tmp;
	}
}
bool Queue::isEmpty()const
{
	return items == 0;
}
bool Queue::isFull() const
{
	return items == qsize;
}
int Queue::queueCount() const
{
	return items;
}
bool Queue::enqueue(const Item& item)
{
	if (isFull())
		return false;
	Node* add = new Node;
	add->item = item;
	add->next = nullptr;
	items++;
	if (front == nullptr)
		front = add;
	else
		rear->next = add;
	rear = add;

	return true;
}
bool Queue::dequeue(Item& item)
{
	if (front == nullptr)
		return false;
	item = front->item;
	items--;
	Node* tmp = front;
	front = front->next;
	delete tmp;
	if (items == 0)
		rear = nullptr;
	
	return true;
}
#include <iostream>
#include<cstdlib>  //rand()  srand()
#include<ctime>   //time()  
#include"Queue.h"
using namespace std;
const int MIN_PER_HR = 60;
bool newCustomer(double x)
{
    return (std::rand() * x / RAND_MAX < 1);
}
int main()
{
    srand(time(0));
    cout << "enter max size of queue\n";
    int qs;
    cin >> qs;
    Queue line(qs);
    cout << "enter simulation time \n";
    int hours;
    cin >> hours;
    long cyclelimt = MIN_PER_HR * hours;
    cout<< "enter the average customer per hour\n";
    double perhour;
    cin >> perhour;
    double min_per_cust;
    min_per_cust = MIN_PER_HR;
    Item tmp;
    long turnaways = 0;
    long customers = 0;
    long serverd = 0;
    int sum_line = 0;
    int wait_time = 0;
    long line_wait = 0;
    for (int cyle = 0; cyle < cyclelimt; cyle++)
    {
        if (newCustomer(min_per_cust))
        {
            if (line.isFull())
            {
                turnaways++;
            }
            else
            {
                customers++;
                tmp.set(cyle);
                line.enqueue(tmp);
            }
        }
        if (wait_time <= 0 && !line.isEmpty())
        {
            line.dequeue(tmp);
            wait_time = tmp.ptime();
            line_wait += cyle - tmp.when();
            serverd++;
        }
        if (wait_time > 0)
            wait_time--;
        sum_line += line.queueCount();
    }

    if (customers > 0)
    {
        cout << "customers accepted :" << customers<<endl;
        cout << "customers serverd :" << serverd << endl;
        cout << "turn away :" << turnaways << endl;
        cout << "average queue size :";
        cout.precision(2);
        cout.setf(ios_base::fixed, ios_base::floatfield);
        cout << (double)sum_line / cyclelimt << endl;
        cout << "average wait time :" << double(line_wait) / serverd << "minutes\n";
    }
    else
    {
        cout << "no customer\n";
    }
    return 0;  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值