《C++编程思想》

《C++编程思想》,第1张

文章目录
  • 书评
  • 阅读方法
  • 代码实战
    • 复现
      • Stash&Stack 1.0 ——简单的结构
      • 访问控制——嵌套友元与迭代器初步
      • Stash&Stack 2.0——添加访问控制
      • 句柄类——封装的封装
      • Stash&Stack 3.0 ——添加构造函数和析构函数
      • SuperVar——Union的重载
      • MyString 1.0——逐字节控制内存工具的应用以及开销的思考
      • StringStack&Quoter——const 综合应用
      • Comm——晦涩的volatile
      • Stash&Stack 4.0——内联函数减少小函数调用开销
        • 补充:内联理解
        • 补充:宏的骚 *** 作
      • 命名空间
      • 引用与拷贝构造函数
        • 引用
        • 默认拷贝构造函数
        • 拷贝构造函数
        • 成员指针
      • *** 作符重载
        • 基本应用
        • operator->/operator->* 与迭代器嵌入
        • operator= 与赋值拷贝
      • PStash——new和delete的动态内存管理
        • new/delete概览
        • PStash
    • 自己写

书评

这本书确实不适合新手上路,尤其是没学过c语言的,所以知乎上大多是喷的。

但是这类人大多没有看过编者按,实际上作者已经明确写出,假定学过c语言,有比较好的基础。

我感觉,要想学懂这本书,至少一本c primer plus是少不了的,因为这本书里面大量讲了编译,链接期间发生的行为,以及堆栈,调用等等系统相关知识,涉及到很多需要反复理解的名词,并且翻译还偶尔有不通顺,如果没点基础,直接劝退呗。

所以个人感觉,要学c++还是看c++ primer好,这本适合在c++的基础上,进一步学习面向对象以及系统知识,还有更高深的编程思想。

阅读方法

我看过c primer plus,也会STL的基本用法,但是第一次看还是有很多地方看不懂,我记忆比较深的就是一些用到寄存器相关知识的,直接给我看破防了,但是还是硬着头皮啃下去了。

所以推荐二遍刷,第一遍用一周时间,全神贯注看,不会就查,有疑惑就敲代码实验,但是不必要看的特别懂,明白作者的意图就不错了。第一遍可以不敲代码,重要的是速通,打个基础,因为这书太高屋建瓴了,二刷才能更好地体会作者的思想。书里有源代码,给代码的有个好处,就是可以靠看来实现速刷,然后二刷来提高熟练度。

第一次刷完了,再代码二刷,全书的代码,最好把习题也连带上,都敲了,如果有别的实战项目也可以自己搞自己的,比如我就是要做这学期的面向对象上机作业,其中一个就是手写一个vector,我打算写个豪华版的,所以这个足够当做练习了。

第二遍要注意抓重点,有些东西就没必要了,或者选择性,比如make的分段编译之类的,如果以后不打算搞开发,学这个也没用,了解一下有这么个东西就行了。二刷的过程,是一次重新理解的过程,可以从更高的层次,以更接近作者思路的层次去理解书本,可以理解一些以前理解不了的东西,同时又是一次重新回忆的过程。

代码实战 复现

这个模块主要是复现书上的,加一点自己的理解,注释。

复现不会选择所有例子,只会选择综合性例子。

Stash&Stack 1.0 ——简单的结构
#include
#include
#include // 系统库,System调用相当于cmd
#include //c++级别字符串
#include //c++级别数组
#include //文件读写
#include //调试
using namespace std;

#ifndef STASH_H
#define STASH_H
const int increment = 100;

struct Stash
{
	int size;//元素大小
	int quantity;//元素个数上限
	int next;//下一个的index

	unsigned char* storage;//按Byte储存空间

	void initialize(int size);
	void cleanup(void);
	int add(const void* elemtent);
	void* fetch(int index);
	int count();
	void inflate(int increase);
};

void Stash::initialize(int sz)//通过size定型
{
	size = sz;
	quantity = 0;
	storage = 0;
	next = 0;
}

int Stash::add(const void* element)
{
	if (next >= quantity)//兼顾从0到1以及从少到多
		inflate(increment);

	int startByte = next * size;
	unsigned char* e = (unsigned char*)element;//转换类型以逐Byte赋值
	for (int i = 0; i < size; i++)
	{
		storage[startByte + i] = e[i];
	}
	next++;
	return (next - 1);
}

void* Stash::fetch(int index)
{
	if (index < 0 || index >= next)
	{
		return NULL;
	}

	return &(storage[index * size]);
}

int Stash::count()
{
	return next;
}

void Stash::inflate(int increase)
{
	if (increase <= 0)//确保increase大于0
	{
		return;
	}

	int newQuantity = quantity + increase;
	int newBytes = newQuantity * size;
	int oldBytes = quantity * size;

	unsigned char* new_s = new unsigned char[newBytes];//申请新空间
	if (!new_s)//确保成功
		return;

	for (int i = 0; i < oldBytes; i++)//逐Byte复制
	{
		new_s[i] = storage[i];
	}

	delete[] storage;//删除原来的
	storage = new_s;
	quantity = newQuantity;
}

void Stash::cleanup()
{
	if (storage != NULL)
	{
		cout << "clean up" << endl;
		delete[] storage;
	}
}

#endif //STASH_H//

int main(void)
{
	//整数测试,装填int 
	Stash intStash;
	intStash.initialize(sizeof(int));
	for (int i = 0; i < 20; i++)
	{
		intStash.add(&i);
	}
	for (int j = 0; j < intStash.count(); j++)
	{
		cout << "intStash.fetch(" 
			<< j << ") = " 
			<< *(int*)intStash.fetch(j) << endl;
	}

	//string测试,装填string对象
	Stash stringStash;
	const int bufsize = 80;//默认string最长长度
	stringStash.initialize(sizeof(char) * bufsize);
	ifstream in("input.txt");
	string line;
	while (getline(in, line))
	{
		//string::c_str,将string对象转化为c-char array
		stringStash.add(line.c_str());//这里装填固定的长度,超出去的是垃圾,提前被}截断
	int
	= k 0 ;char
	*; cpwhile
	( (=cp ( char*).stringStashfetch(++k))!= NULL )<<
	{
		cout "stringStash.fetch(" << << k ") = " << << cp ; endl}
	.

	intStashcleanup();//防止内存泄露 .
	stringStashcleanup();return
	
	0 ;}
#
include#
include#
include// 系统库,System调用相当于cmd #
include//c++级别字符串 #
include//c++级别数组 #
include//文件读写 #
include//调试 using
namespace ; std#

ifndefSTACK_H  //防止重复声明结构,函数没有这个问题#
defineSTACK_H struct
Stack //嵌套结构,顺便定义个成员 {
	//注意这个链表不同于常规链表,里面的data是指针,这是为了不同类型
	struct
	Link void {
		*; data*
		Link; nextvoid
		initialize (void*, dat* Link) nxt;}
	*;headvoid 

	initialize ();void
	push (void*) data;void
	*peek ();void
	*pop ();void
	cleanup ();}
;void

:: StackLink::initialize(void*, dat* Link) nxt//节点初始化=
{
	data ; dat=
	next ; nxt}
void

Stack ::initialize()//初始化头部为NULL=
{
	head NULL ;}
void

Stack ::push(void*) dat//将新节点插到头部,注意是直接把元素插进去了,没有复制 *
{
	Link= node new ; Linkinitialize
	node->(,dat) head;=
	head ; node}
void

*Stack ::peek()//获取顶部元素 if
{
	( ==head NULL )//判断空 return
		NULL ;return

	; head->data}
void

*Stack ::pop()if
{
	( ==head NULL )//判断空return
		NULL ;void

	*= result ; head->data//暂时储存 *
	Link= old_head ; head=
	head ; head->next//移位 delete
	; old_head//处理暂存 return
	; result}
void

Stack ::cleanup()<<
{
	cout "clean up" << ; endl}
#
endif//STACK_H// int

main (void)in
{
	ifstream ("input.txt");;
	Stack lineStack.
	lineStackinitialize();;

	string linewhile
	( getline(,in) line).
	{
		
		lineStackpush(newstring ()line);//拷贝构造一份push进去//lineStack.push(&line); 
		/*
		//这样会有问题,可以自己画个指针图来求解
		//cout << &line << endl; //line的地址不变
		//三个data都指向了line变量的内存位置,而line最后变为NULL
		//后面的delete NULL就会报错
		*/
		}
	*

	string; lpwhile
	( (=lp ( *string).lineStackpop())!= NULL )<<
	{
		cout * <<lp ; endldelete
		; lp//lp得到的是data的指针,用完元素记得清理}
	.

	lineStackcleanup();return

	0 ;}
#
访问控制——嵌套友元与迭代器初步
include#
include#
include// 系统库,System调用相当于cmd #
include//c++级别字符串 #
include//c++级别数组 #
include//文件读写 #
include//调试 using
namespace ; stdconst

int = sz 20 ;//嵌套友元与迭代器初步

struct
Holder private {
:int
	[ a]sz;public
:void
	initialize ()memset
	{
		(,a0 ,* sz sizeof (int));}
	struct

	Pointer ;//不完全声明 friend
	; Pointer//仅嵌套结构,不进行组合,实际上是一个迭代器,之所以嵌套是为了表达所属关系
	struct
	Pointer private {
	:*
		Holder; hint
		*; ppublic
	://函数全部内联,因为太短了
		void
		initialize (*Holder) rv=
		{
			h ; rv//绑定Holder=
			p ; h->a//获取Holder容器的访问权限 }
		//以下为指针移动与存取 *** 作,通过Pointer来间接实现
		void
		next ()if
		{
			( <p & ([h->a-sz 1 ]))//通过地址判断越界++
			{
				p;}
			}
		void
		previous ()if
		{
			( &p > ([h->a0]))--
			{
				p;}
			}
		void
		top ()=
		{
			p & ([h->a0]);}
		void
		end ()=
		{
			p & ([h->a-sz 1 ]);}
		int

		read ()return
		{
			* ;p}
		void
		set (int) i*
		{
			=p ; i}
		}
	;}
;int

main (void);
{
	Holder h::
	Holder,Pointer hp; hp2int
	; i.

	hinitialize();.
	hpinitialize(&)h;.
	hp2initialize(&)h;for

	( =i 0 ;< i ; sz++ i).
	{
		hpset()i;.
		hpnext();}
	.

	hptop();//双指针头尾反向遍历.
	hp2end();for
	( =i 0 ;< i ; sz++ i)<<
	{
		cout "hp = " << . hpread()<<
			", hp2 = " << . hp2read()<< ; endl.
		hpnext();.
		hp2previous();}
	return

	0 ;}
//就是简单的加一下access specifier即可,实现不需要改变,成员函数随便访问private
Stash&Stack 2.0——添加访问控制
#
include#
include#
include// 系统库,System调用相当于cmd #
include//c++级别字符串 #
include//c++级别数组 #
include//文件读写 #
include//调试 using
namespace ; std#

ifndefSTASH_H #
defineSTASH_H const
int = increment 100 ;struct

Stash public
{
:void
	initialize (int) size;//定型 void
	cleanup (void);//清空int
	add (constvoid *) elemtent;//添加  void
	*fetch (int) index;//读取 int
	count ();//查询数量private
:int

	; size//元素大小int
	; quantity//元素个数上限int
	; next//下一个的indexunsigned
	char *; storage//按Byte储存空间void

	inflate (int) increase;//扩容方法}
;#
include#
include#
include// 系统库,System调用相当于cmd #
include//c++级别字符串 #
include//c++级别数组 #
include//文件读写 #
include//调试 using
namespace ; std#

ifndefSTACK_H  //防止重复声明结构,函数没有这个问题#
defineSTACK_H struct
Stack //嵌套结构,顺便定义个成员 {
	//注意这个链表不同于常规链表,里面的data是指针,这是为了不同类型
	public
:void
	initialize ();void
	push (void*) data;void
	*peek ();void
	*pop ();void
	cleanup ();private
:struct
	Link void {
		*; data*
		Link; nextvoid
		initialize (void*, dat* Link) nxt;}
	*;head}
;/*
句柄类用法,用一个只有public方法的类将一个具有private方法的类包装起来
使用不完全定义的嵌套类,并将其指针包含进去,只暴露指针
好处1:隐藏
好处2:修改结构成员和private都不会影响头文件,不使用句柄类就得同时重新编译头文件和目标程序,现在只需要编译头文件即可,目标程序因为头文件不变不需要重新编译.
*/
句柄类——封装的封装
#
include#
include#
include// 系统库,System调用相当于cmd #
include//c++级别字符串 #
include//c++级别数组 #
include//文件读写 #
include//调试 using
namespace ; std#

ifndefHANDLE_H #
defineHANDLE_H class
Handle public {
:void
	initialize ();void
	cleanup ();int
	read ();void
	change (int);private

:struct
	Cheshire ;//不完全定义 *
	Cheshire; smile//用户只能看到这个,至于指向的地方是什么样的(源代码)无法把握 }
;//以下为自己放在别处的实现

struct
Handle ::Cheshire//这里可以加各种private,这里仅仅简单做个类 { int
	; i}
;void
Handle ::initialize()=
{
	smile new ; Cheshire=
	smile->i 0 ;}
void
Handle ::cleanup()delete
{
	; smile}
int

Handle ::read()return
{
	; smile->i}
void

Handle ::change(int) x=
{
	smile->i ; x}
#
endif//HANDLE_H// int

main (void);
{
	Handle u.
	uinitialize();<<
	cout . uread()<< ; endl.
	uchange(1);<<
	cout . uread()<< ; endl.
	ucleanup();return

	0 ;}
#
Stash&Stack 3.0 ——添加构造函数和析构函数
include#
include#
include// 系统库,System调用相当于cmd #
include//c++级别字符串 #
include//c++级别数组 #
include//文件读写 #
include//调试 using
namespace ; std#

ifndefSTASH_H #
defineSTASH_H const
int = increment 20 ;class
Stash public {
:Stash
	(int) size;~
	Stash();int
	add (constvoid *) element;void
	*fetch (int) index;int
	count ();private

:int
	; sizeint
	; quantityint
	; nextunsigned
	char *; storagevoid

	inflate (int) increase;}
;Stash

::Stash(int) sz: size()sz,quantity (0),storage (NULL),next (0)<<
{
	cout "stash constructor" << ; endl}
Stash

::~Stash()<< {
	cout "stash destructor" << ; endl}
int

Stash ::add(constvoid *) elementif
{
	( )next >= quantity//兼顾从0到1以及从少到多inflate
		()increment;int

	= startByte * next ; sizeunsigned
	char *= e ( unsignedchar *);element//转换类型以逐Byte赋值for
	( int= i 0 ;< i ; size++ i)[
	{
		storage+startByte ] i= [ e]i;}
	++
	next;return
	( -next 1 );}
void

*Stash ::fetch(int) indexif
{
	( <index 0 || ) index >= nextreturn
	{
		NULL ;}
	return

	& ([storage*index ] size);}
int

Stash ::count()return
{
	; next}
void

Stash ::inflate(int) increaseif
{
	( <=increase 0 )//确保increase大于0return
	{
		;}
	int

	= newQuantity + quantity ; increaseint
	= newBytes * newQuantity ; sizeint
	= oldBytes * quantity ; sizeunsigned

	char *= new_s new unsigned char []newBytes;//申请新空间if
	( !)new_s//确保成功return
		;for

	( int= i 0 ;< i ; oldBytes++ i)//逐Byte复制[
	{
		new_s]i= [ storage]i;}
	delete

	[]; storage//删除原来的=
	storage ; new_s=
	quantity ; newQuantity}
#

endif//STASH_H// int


main (void)//整数测试,装填int 
{
	intStash
	Stash (sizeof(int));for
	( int= i 0 ;< i 20 ;++ i).
	{
		intStashadd(&)i;}
	for
	( int= j 0 ;< j . intStashcount();++ j)<<
	{
		cout "intStash.fetch(" <<
			<< j ") = " <<
			* (int*).intStashfetch()j<< ; endl}
	//string测试,装填string对象

	const
	int = bufsize 80 ;//默认string最长长度stringStash
	Stash (*bufsizesizeof(char));in
	ifstream ("input.txt");;
	string linewhile
	( getline(,in) line)//string::c_str,将string对象转化为c-char array
	{
		.
		stringStashadd(.linec_str());//这里装填固定的长度,超出去的是垃圾,提前被}截断int
	=
	0 k ; char*
	;while cp(
	( =(cp char *).fetchstringStash(++)k)!=NULL ) <<"stringStash.fetch("
	{
		cout << << ") = " k << << ; cp } endl//析构执行点
	return

	0
	; }#
include
#include
#include
// 系统库,System调用相当于cmd# include
//c++级别字符串# include
//c++级别数组# include
//文件读写# include
//调试using namespace
; # stdifndef

STACK_H# define
STACK_Hclass Stack
public : {
Stack(
	);~Stack
	();voidpush
	( void*); datvoid*
	peek( );void*
	pop( );private:

structLink
	void * {
		;* data;
		LinkLink next(
		void*,* dat) Link; nxt//所有类都应该有四大函数~ Link
		();}*
	;}head;
::Link

Stack::Link(void*,* dat) Link: nxtdata (),datnext( )<<nxt"Stack::Link "
{
	cout << <<" constructor"data<<; } endl::
Link

Stack::~Link()<<"Stack::Link "
{
	cout << <<" destructor"data<<; } endlStack
::

Stack():head (NULL)<<"Stack constructor"
{
	cout << ; } endlStack
::

~Stack()<<"Stack destructor "
{
	cout << ( == NULLhead ? "success":"fail")<<;}endlvoid
Stack


:: push(void*)//将新节点插到头部,注意是直接把元素插进去了,没有复制 dat* =
{
	Linknew node Link ( ,)dat; head=;
	head } nodevoid
*

Stack:: peek()//获取顶部元素if (
{
	== NULLhead ) //判断空return NULL
		; return;

	} head->datavoid
*

Stack:: pop()if(
{
	== NULLhead ) //判断空returnNULL
		; void*

	=; result //暂时储存 head->data* =
	Link; old_head = head;
	head //移位 head->nextdelete ;
	//处理暂存 old_headreturn ;
	} result#
endif


//STACK_H//int main
( void)in(
{
	ifstream "input.txt");;//构造函数没有参数就不用带括号了
	Stack lineStack;while

	string line(
	getline (,)in) line.push
	{

		lineStack(newstring( ))line;//拷贝构造一份push进去//lineStack.push(&line); /*
		//这样会有问题,可以自己画个指针图来求解
		//cout << &line << endl; //line的地址不变
		//三个data都指向了line变量的内存位置,而line最后变为NULL
		//后面的delete NULL就会报错
		*/
		}
		*
	;

	stringwhile lp(
	( =(lp * )string.poplineStack())!=NULL ) <<*
	{
		cout << ;lp delete endl;
		//lp得到的是data的指针,用完元素记得清理,之所以不将清理内存放在 lp//析构函数中,是因为传出来的不是拷贝,而是指针,这个程序的根本性问题就在于//全都是用的指针,即使是在一些应该有拷贝的地方,这就极易出问题
		}
		return
	0

	; }#
include

SuperVar——Union的重载

其实没啥用,因为已经失去了Union最本真的用途了,只是演示Union也可以像类一样有四大函数,也可以重载构造函数.

#include
#include
// 系统库,System调用相当于cmd# include
//c++级别字符串# include
//c++级别数组# include
//文件读写# include
//调试using namespace
; class stdSuperVar

public : {
SuperVar(
	char): chc(),chvartype()<<character"SuperVar constructer char"
	{
		cout << ; } endlSuperVar
	(
	int): iii (),iivartype()<<integer"SuperVar constructer int"
	{
		cout << ; } endlSuperVar
	(
	float): fff (),ffvartype()<<floating_point"SuperVar constructer float"
	{
		cout << ; } endlvoid
	print
	
	( )switch(
	{
		) casevartype:
		{
		<< character"char: "
			cout << << ; c break endl;
			case:
		<< integer"int: "
			cout << << ; i break endl;
			case:
		<< floating_point"float: "
			cout << << ; f break endl;
			default:
		<<"error"
			cout << ; } endl}
		private
	:

enum//匿名枚举
	, { ,
		character}
		integer;
		floating_point
	unionvartype//匿名联合,可以直接 *** 控变量

	char {;
		int c;
		float i;
		} f;
	};
intmain

( void)A(
{
	SuperVar 'c'),B( 1),C( 1.2f);//这里必须通过f后缀声明floatD (
	SuperVar float(1.2));//或者显式转化. print

	A();.print
	B();.print
	C();.print
	D();return0

	; }#

include
MyString 1.0——逐字节控制内存工具的应用以及开销的思考

其实之前的stash和stack都可以用这个做,char的另一个优势在于可以用strcpy,strcat等各种函数,以及memset,memcpy等等,估计这些也是用底层内存写的。

#include
#include
// 系统库,System调用相当于cmd# include
//c++级别字符串# include
//c++级别数组# include
//文件读写# include
//调试using namespace
; # stdifndef

MEM_H # define
MEM_H//一个辅助内存块管理工具类 typedef unsigned
char ; //基本内存单位 byteclass Mem

public : {
//不使用重载而是默认参数,因为这两个没有开销区别/*Mem() :mem(0), size(0)
	{
		cout << "Mem constructor" << endl;
	}*/
	Mem
	(
	int=0 sz):mem (0),size( 0)//分配sz的空间<< "Mem constructor "
	{
		cout << << " Bytes" sz << ; ensureMinSize endl(
		);sz}~
	Mem
	()<<"Mem destructor"
	{
		cout << ; delete endl[
		];//可以delete NULL mem} int
	getSize
	( )//总空间return ;
	{
		} size*
	pointer
	byte( )//返回空间指针,有需要可以转化为各种格式的指针对应类型return ;
	{
		} mem*
	pointer
	byte( int)//返回前确保空间 minSizeensureMinSize (
	{
		);minSizereturn;
		} memprivate
	:

*;
	byteint mem;
	//剩余空间 sizevoid ensureMinSize
	( int); minSize};
voidMem


:: ensureMinSize(int)if minSize(
{
	< )size //空间不足 minSize//新空间申请与清理,只清理后面的空间是因为前面的会被覆盖,没必要 *
	{   =
		bytenew newmem [ ] byte;minSizememset( 
		+,newmem 0 size, -) minSize ; size//转移处理老空间memcpy
		(
		,,newmem) mem; sizedelete[
		];//更新 mem=
		;
		mem = newmem;
		size } minSize}
	#
endif

//MEM_H//# ifndef

MYSTRING_H# define
MYSTRING_Hclass MyString
public : {
MyString(
	):buf (NULL)<<"MyString constructor" 
	{
		cout << ; } endlMyString
	(
	constchar* )<< str"MyString constructor"
	{
		cout << ; = endlnew
		buf Mem ( strlen()+str1 ) ;//多一个strcpy的空间( (
		char*)pointer()buf->,);//将空间视作char* str}~ MyString
	(
	)<<"MyString destructor"<<
	{
		cout ; delete ; endl//调用Mem析构函数,析构调用是递归的
		} bufvoidconcat
	(
	const char*) //定义空串行为if str(
	{
		!
		) =newbuf;
		{
			buf } //确保空间具有 原来串+新串长度+1的空间 Memstrcat
		(
		(
		char*)pointer(getSizebuf->()buf->+strlen( ) +1str) , );} strvoidprint
	(
	& =)ostream//将串输出到目标位置,默认为cout osifcout( !
	{
		) return;buf}
		{
			<<pointer
		(
		os ) buf-><<;} private endl:
	*

;//指向一个Mem类,Mem类里面有指针指向空间

	Mem} buf; #
endif//MYSTRING_H//

intmain (

void )s("My test string"
{
	MyString );.print(

	s);.concat(
	s" some additional stuff");.print(
	s);;.concat
	
	MyString s2(
	s2"Using defalult constructor");.print(
	s2);return0;

	} #include
#
StringStack&Quoter——const 综合应用
include#
include// 系统库,System调用相当于cmd
#include //c++级别字符串
#include //c++级别数组
#include //文件读写
#include //调试
usingnamespace ;
# ifndef stdSTRINGSTACK_H

#define STRINGSTACK_H
classStringStack public

: StringStack {
()
	:index( 0)memset(,
	{
		0,stack* sizeof( size * ))string;}voidpush
	(
	
	const *)//确保不会修改,所以用const指针,但是仅仅是我们这个指针const string//其他地方指向同样string的可能修改这个string sif 
		(
		<
	{
		) [index ++ size]
		{
			stack=index;} } sconst
		*
	pop
	( string) //不允许返回值修改,所以返回const对象if( 0
	{
		) constindex > *=
		{
			[ string-- rv ] stack;//现在rv和stack[index]同时指向stringindex[]=
			stackNULLindex; //释放一个指针 return;}
			return rvNULL
		;
		} private:
	static

constint
	= 100 ; size //static将类中const放到编译期间,全局const也在编译期间 const*[
	] string; stack//size个const指针sizeint; //指向下一位
	} index;#
endif//STRINGSTACK_H

intmain (

void )[]=
{
	
	string iceCream"1,", "2," 
	{
		,"3,","4,"};//自动计算长度
	constint
	=
	sizeof ( iCsz ) /sizeoficeCream( * );;iceCreamfor(

	StringStack ssint
	= 0; i < ;++ i ) iCsz. ipush(
	{
		ss&[])iceCream;i//非const赋const}const*
	;
	while string( cp(
	= .popcp ( ss))!=NULL) << *<<
	{
		cout ; }cp //以下证明const指针仅仅保护通过当前指针的路径不被修改,别的路还可以修改 endl[
	2

	]
	iceCream="changed by no-const pointer"; for (int

	= 0; i < ;++ i ) iCsz. ipush(
	{
		ss&[])iceCream;i//非const赋const,从iceCream可以修改,但是不可以从ss修改}while(
	(

	= .popcp ( ss))!=NULL) << *<<
	{
		cout ; }cp return endl0
	;

	} #include
#
include#
include// 系统库,System调用相当于cmd
#include //c++级别字符串
#include //c++级别数组
#include //文件读写
#include //调试
#include //时间相关
usingnamespace ;
class Quoter stdpublic

: Quoter {
()
	;intlastQuote(
	) const;//const成员函数,当对象被声明为const就不能调用非const方法 constchar *
	quote () ;private:int

;}
	; lastquoteQuoter
::Quoter

():lastquote(-1)srand(time
{
	(0));}intQuoter
::

lastQuote ()const//声明和定义都要加const,统一仍然适用return ; }
{
	const lastquotechar
*

Quoter ::quote ()//返回一个const*,但是会改变成员(lastquote)staticconstchar
{
	* [ ]= quotes//static const,编译期间量"quote-1" , { "quote-2"
		,"quote-3"
		,"quote-4"
		,"quote-5"
		};
		const
	int=
	sizeof / qsize sizeof * quotes ; int = quotesrand

	( qnum ) %;//随机获取一个范围内下标 while qsize( 0
	&& ==lastquote >= ) //不取和上一次相同的或者-1 qnum = lastquoterand(
	{
		qnum ) %;} return qsize[
	=

	] quotes;lastquote } qnumintmain
(


) ;const;
{
	Quoter q.
	lastQuote Quoter cq(
	cq);// cq.quote() 压根不会出现在备选列表里for(
	int
	= 0; i < 20; i ++ )//q就可以调用,因为q不是const对象 i<<. quote
	{
		cout ( q)<<;} return endl0
	;

	} #include
#
Comm——晦涩的volatile

这个初学不会用,volatile的意思是不要让编译器进行想当然优化,也就是我们做好了出现意外情况的思想准备。比如我已经做好了被多线程修改的准备,就不要让编译器拒绝多线程。

include#
include// 系统库,System调用相当于cmd
#include //c++级别字符串
#include //c++级别数组
#include //文件读写
#include //调试
#include //时间相关
usingnamespace ;
class Comm stdpublic

: Comm {
()
	;voidisr(
	) volatile;char read(
	int )const; indexprivate :enum

=100
	}{bufsize;//等效 *** 作//static const int bufsize = 100;constvolatile
	unsigned
	char ; volatile unsigned bytechar
	; unsigned char flag[
	
	] ; bufintbufsize;}
	; indexComm
::Comm

():index( 0),byte(0 ),flag(0 )<<"Comm constructor"<<
{
	cout ; } void endlComm
::

isr ()volatile=0 ;
{
	flag [ ++]
	buf=index;if ( byte)

	= 0index >= bufsize;
	{
		index } }char
	Comm
::

read (int)constif index( <
{
	0 ||index ) return 0 index >= bufsize;
	{
		} return[
	]

	; buf}indexintmain
(

void )volatile;.
{
	isr Comm Port(
	Port);//Port.read(0); //不行,不是volatilereturn0
	;

	} #include
#
Stash&Stack 4.0——内联函数减少小函数调用开销
include#
include// 系统库,System调用相当于cmd
#include //c++级别字符串
#include //c++级别数组
#include //文件读写
#include //调试
usingnamespace ;
# ifndef stdSTASH_H

#define STASH_H
constint =
20 ; increment class Stashpublic
: Stash {
(int
	):size sz( ),quantitysz(0 ),storage(NULL ),next(0 )<<"stash constructor"<<
	{
		cout ; } ~ endlStash
	(
	)<<"stash destructor"<< {
		cout ; } int endladd
	(
	const void*) ;//非内联 elementvoid* fetch
	(int )if( index<
	{
		0 ||index ) return NULL index >= next;
		{
			} return&
		(

		[ *]storage)index ; size}intcount
	(
	) return;}
	{
		private next:
	int

;int
	; sizeint
	; quantityunsigned
	char next*
	; voidinflate storage(

	int );//非内联 increase};int
Stash::


add (constvoid*) if( element)
{
	//兼顾从0到1以及从少到多 inflatenext >= quantity()
		;intincrement=*

	; startByte unsigned next char size*
	= (unsigned e char *) ;//转换类型以逐Byte赋值forelement(int
	= 0; i < ;++ i ) size[ i+]
	{
		storage=startByte [ i] ; e}i++;
	return
	next(-
	1 )next ; }voidStash
::

inflate (int)if( increase<=
{
	0 )increase //确保increase大于0 return;}
	{
		int=
	+

	; newQuantity int quantity = increase*
	; newBytes int newQuantity = size*
	; oldBytes unsigned quantity char size*

	= newunsigned new_s char [ ] ;//申请新空间newBytesif(!
	) //确保成功returnnew_s;for
		(int

	= 0; i < ;++ i ) oldBytes//逐Byte复制 i[]=
	{
		new_s[i] ; storage}idelete[
	]

	;//删除原来的= storage;=
	storage ; new_s}
	quantity # newQuantityendif
//STASH_H//

intmain (


void )//整数测试,装填int intStash(
{
	sizeof
	Stash (int));for(int
	= 0; i < 20; i ++ ). iadd(
	{
		intStash&);}ifor(
	int
	= 0; j < .count j ( intStash);++)<< j"intStash.fetch("<<
	{
		cout << ") = "
			<< j * (
			int *).fetch(intStash)<<;j} //string测试,装填string对象 endlconst
	int

	=
	80 ; bufsize //默认string最长长度 stringStash(*
	Stash sizeof(bufsize char ));in("input.txt"
	ifstream );;while(
	string linegetline
	( ,))in//string::c_str,将string对象转化为c-char array line.add
	{
		(
		stringStash.c_str(line));//这里装填固定的长度,超出去的是垃圾,提前被}截断int=0
	;
	char k * ;while
	(( cp=
	( char*cp ) .fetch(++stringStash))!=kNULL)<< "stringStash.fetch(" <<<<
	{
		cout ") = " << << k ; } //析构执行点 cp return endl0
	;

	}
	# include#
include
#include
// 系统库,System调用相当于cmd#
include//c++级别字符串 #
include//c++级别数组 #
include//文件读写 #
include//调试 using
namespace; #
ifndef STACK_H std#

defineSTACK_H class
Stackpublic :
Stack ( {
):
	head(NULL )<<"Stack constructor"<<;
	{
		cout } ~ Stack endl(
	)
	<<"Stack destructor "<<(
	{
		cout == NULL ? "success"head : "fail" ) << ; }void push endl(
	void
	* )//将新节点插到头部,注意是直接把元素插进去了,没有复制*= datnew Link
	{
		Link( node , ) ;=dat; head}void
		head * nodepeek
	(
	)//获取顶部元素 if(== NULL
	{
		) //判断空head return NULL; return
			; }void

		* head->datapop
	(
	)//这个也不算大,就内联了 if(== NULL
	{
		) //判断空head return NULL;void
			* =;

		//暂时储存* result = head->data; =
		Link; old_head //移位 headdelete
		head ; head->next//处理暂存 return
		; old_head} private
		: resultstruct
	Link

void*
	; * {
		;Link data(
		Linkvoid next*
		,*): datdata Link( nxt) ,next(dat)//所有类都应该有四大函数 <<"Stack::Link "nxt<<<<
		{
			cout " constructor" << ; data } ~ Link endl(
		) 
		<<"Stack::Link "<<<<
		{
			cout " destructor" << ; data } } * endl;
		}
	;#headendif
//STACK_H//int

main( void

) in("input.txt")
{
	ifstream ;;//构造函数没有参数就不用带括号了;while
	Stack lineStack(getline

	string line(
	, )).inpush line(new
	{

		lineStackstring()) ;//拷贝构造一份push进去line//lineStack.push(&line); /*
		//这样会有问题,可以自己画个指针图来求解
		//cout << &line << endl; //line的地址不变
		//三个data都指向了line变量的内存位置,而line最后变为NULL
		//后面的delete NULL就会报错
		*/}*
		;
		while
	(

	string( lp=
	( *)lp . popstring()lineStack)!=NULL)<< * <<;
	{
		cout delete ;lp //lp得到的是data的指针,用完元素记得清理,之所以不将清理内存放在 endl//析构函数中,是因为传出来的不是拷贝,而是指针,这个程序的根本性问题就在于
		//全都是用的指针,即使是在一些应该有拷贝的地方,这就极易出问题 lp}return
		0
		;
	}

	class Xpublic
:

补充:内联理解

普通函数有自己的内存,地址,需要进行调用,而调用这一步具有比较大的开销,如果能直接将函数调用替换为源代码就好了,这实际上就是类似于宏的用法。

对于宏,宏其实是保存在符号表里的,占用空间,内联函数不仅有宏一样的特性,储存在符号表,然后替换,可以有效减少调用开销,而且还具有编译器的类型检查功能,这是宏没有的。代价就是占用符号表空间,如果内联函数太大,空间占用带来的替换开销或许就会超过调用的开销。

所以有一个原则,小函数内联,大函数和带循环的(展开后代码膨胀)调用。形式上没什么区别,本质上区别在于宏与非宏。如果要在性能上下功夫,内联是可以考虑的一个点,实际上比较复杂。

需要注意的是,inline只是一个建议,类似于register,编译器会自己进行判断是否采纳。

void common_func {
()
	; //普通调用函数voidinline_out_class( )
	; voidfunc_inline_in_class()
	//内部直接写默认内联 <<"inline"<<;
	{
		cout } } ; endlvoid
	X
::common_func
( )<<"common function"<<;
{
	cout } inline void endlX
::
inline_out_class ( )//外部内联,需要inline<<"inline"<<;
{
	cout } 
  • 字符串定义
  • 字符串转换——用于字符串拼接
  • endl
  • 标志符粘贴
  • #
    补充:宏的骚 *** 作
      include#include
    #include
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; //定义函数语句最后的;不用加,因为用的时候会加
    # define stdSTRING
    
    "nihao"
    //字符串定义# define DEBUG (
    )<< " = "<<x<< cout //字符串拼接,#将标志符直接变成字符串 #x # define x TRACE endl (
    )<< <<;s# cerrdefine#sLABELendl( s 
    )## =//标志粘贴,##将i直接替换到标志符,提供代码级的重用iint string label_maini(#i void
    
    ) <<"字符串定义: "<<<<
    {
    	cout ; <<"字符串拼接:"STRING ; endl=
    	cout "nihaoya" ;DEBUG
    	string str();
    	<<"TRACE: "str;TRACE
    	cout ( );
    	LABEL(str1)
    
    	;LABEL(2)
    	;<<"label_1: "<<<<
    
    	cout " label_2: "<< << label_1 ; return 0 label_2 ; endl}
    
    	# include#
    include
    
    命名空间

    这个别想了,初学没用处的,这是做大项目才会用到的,最多学一下using指令。

    引用与拷贝构造函数 引用

    引用的基本概念比较简单,就是用法需要注意一点。

    const是需要注意的,如果不需要改,或者营造出一种传值的感觉,那就用const引用,否则就是要改了,要改的话用引用有可能引起混淆,因为外面的人不知道你是按值传递还是传引用。

    所以按值传递就无脑const引用即可

    要改的话就得想想用引用还是指针

    默认拷贝构造函数

    常理来说,拷贝构造应该是直接复制一份,如下实验说明了这个假设:

    #include
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; //测试默认拷贝构造函数
    class X stdpublic
    
    :
    
    * ; {
    };
    	stringint pmain
    (void
    
    ) ;="nihao";
    {
    	X x.
    	string str = &;
    	x=p ; //默认cc函数str<<
    
    	X y "xp: " x<< .
    	cout << ; << x"yp: "p << endl.
    	cout << ; return y0p ; endl}
    
    	
  • 目的。定义按值传递时的行为
  • 写法。同构造函数,只不过参数只有一个,class &,有时候会是const class &,其实个人感觉const更好,因为拷贝构造一般不会修改原有对象的。
  • 为什么是传引用呢?一方面比较有效率,另一方面,拷贝构造函数就是为了定义类按值传递时的行为,不用引用的话,你还没定义呢,你就按值传递了。
  • 初始化列表。不是构造函数就是拷贝构造函数,说白了,构造和拷贝构造是同级的,只不过场景不同。
  • 可以看到,两个指针的内容一样,这就是默认的CC函数,符合假定,也就是按位拷贝。

    但是这样也会带来一个问题,就是不适合处理复杂的类,比如带指针的。你光是复制了指针,不复制人家指向的对象,就不行,所以得自己编写拷贝构造函数来实现复杂的 *** 作。

    拷贝构造函数
    1. 特殊应用:私有拷贝构造。这个可以阻止按值传递。
    2. #include#include
    成员指针

    本质:定义的时候表示一种相对于基地址的偏移,针对一个类,而不针对某个对象

    所以没有精确指向某个对象的成员的指针,只有指向一个类中成员的指针,然后配合对象或者对象指针来使用。

    #include
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; class
    Data public std:
    
    int , {
    , ;
    	void aprint b( c)
    	const <<"a = "<< <<
    	{
    		cout ", b = " << << a ", c = " << << b ; } } c ; endlint
    	main
    (void
    
    
    ) ;*=&
    {
    	Data d;
    	Data//重点解析这个定义式 dp /*
    	不过是在 *指针名 前面加个作用域,表示针对这个类
    	前面的类型表明只针对这种类型的成员
    	右边规定了指针指向类中哪个成员,写法仅仅符合指针写法,不具有取地址实际意义
    	*/ intd::
    
    	*
    	=
    	& Data::; memPointerInt . *Data=a1
    	d;//点号用法memPointerInt = &:: ;
    
    	memPointerInt //更换指向成员 *Data=b2;
    	dp->=memPointerInt & ::;
    	memPointerInt . *Data=c3
    	d;//函数成员指针类似,也是加个作用域,右边仅针对一类memPointerInt void (::
    	
    	*
    	) (Data) const funcPointer=&:: ; ( .Data*print)
    	(d);funcPointer//这里要注意前面整体相当于函数名,要括起来return0; }
    
    	
  • 成员函数不需要传自己,并且可以使用this进行对自己的引用和 *** 作。
  • this返回当前对象指针,*this返回当前对象(外面一般用引用承接)
  • 一般都是const &的,因为基本都是仅仅用值
  • 不修改返回值,且返回值为原对象。const class & 理由:返回原有对象,引用高效,const防止修改。
  • *** 作符重载 基本应用

    核心应用场景:

    将内建数据类型的 *** 作符 *** 作移植到自定义类里。比如int 和 Integer类

    传入参数:

    1. 返回值作为临时量。const class 理由:一般用于返回新对象。不能传引用,因为临时量生存期很短。
    2. 修改返回值。class & 理由:返回原有对象继续修改。
    3. 重载中大量使用const 返回值,所以其成员函数,能const尽量const,不然 *** 作不了这个返回的临时量。

    关于返回值:

    返回值取决于我要返回什么,我要对我的返回值做什么。

    1. 遵循直观原则,一元就成员,二元就全局。
    2. 必须是成员函数且只能接受一个索引参数
    3. 很常用。

    注意const成员函数:

      #

    是否全局:

      include
    1. 特殊的,比如= () [] -> ->*,这种具有赋值和选择含义的,因为总是伴随对象,所以只能作为成员。

    索引符:

      #include
    
    #include
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; #
    ifndef INTEGER_H std#
    
    defineINTEGER_H //整数, *** 作符重载包装类
    classInteger public :
    Integer ( {
    long=
    	0): ll i () <<"Integer c"<<ll;
    	{
    		cout } long getValue endl(
    	)
    
    	const //const函数主要是针对临时量的,否则(++a).getValue就无效return; } //全局函数,需要传入全部参数,给个friend
    	{
    		friend iconst
    	&
    
    	//传入a引用,返回a,所以全用const引用效率最高,能用const&最好
    	operator + Integer( const
    		& ) ;friend Integerconst a//二元写成全局更直观operator
    
    	+ ( Integer const
    		&,const& Integer) left; //成员函数,自己就不用传入了,不用friend Integerconst right//返回的是一个临时量,所以没办法用引用,注意是没办法才按值传递的。operator
    
    	-
    
    	( Integer )
    		return Integer (-
    	{
    		) ;//这里隐含返回值优化,可以直接将临时量创建在外面的承接空间中//从而省去内部量的一次构造和析构i}const&
    		operator
    	++
    	( Integer)
    		++ ;return*
    	{
    		ithis;
    		//返回当前对象引用的方法 }const//同理,因为返回临时量,所以只能按值传递 operator
    	++
    	( Integer int
    		) before(*this
    	{
    		Integer );//CC函数++;return;
    		i//返回临时量}
    		private before:long
    	;
    
    	
    
    *This
    	( i)
    
    	Integerreturn this;}
    	{
    		} ;const
    	&
    operator+
    
    ( Integerconst 
    & ) return; Integer//传入引用,返回引用,不变 a}
    {
    	const aoperator+
    (
    
    const Integer
    & , const& Integer) leftreturn Integer Integer( right.
    {
    	+ .)left;i } right#iendif//INTEGER_H//
    int
    
    
    main( void
    
    ) a(1)
    {
    	Integer ;<<"a "<<.
    	cout getValue() a<<;<<"+a " << endl+
    
    	cout .getValue( ) a<<;<<"-a " << endl-
    
    	cout . getValue ( )a<<;<<"++a " << endl(
    
    	cout ++ ) . getValue(a)<<;<<"a++ " << endl(
    
    	cout ++ ) . getValuea()<<;<<"a++ " << endl.
    
    	cout getValue ( ) a<<;return0 ; endl}
    
    	
  • 必须返回一个对象,其也可以进行间接引用,这样保证了链式调用。
  • 或者返回最终指向的类型,这就标志着到达终点。
  • # include
    operator->/operator->* 与迭代器嵌入

    这个也只能是成员函数,这种形式,就意味着有类似于指针的行为,被叫做灵巧指针,常用于迭代器。

    有两个规定:

      #include
    #include 
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; //
    class Obj stdpublic
    
    :
    void f {
    ()
    	const <<++<< ;
    	{
    		cout } ivoid g endl(
    	)
    	const <<++<< ;
    	{
    		cout } jprivate : endlstatic
    	int
    
    ,;
    	//所有Obj公用这两个 } i; jint ::
    =47
    
    ; Objinti :: =11
    ; Obj//容器j class ObjContainerpublic
    
    :
    void add {
    (*
    	) //添加成员.Objpush_back obj()
    	{
    		a;}classobjSmartPointer;
    	//迭代器,用于访问,相当于将一些公共接口又封装了一次
    		
    	friend ;//打开内部类向外 *** 作路径 class
    	SmartPointer SmartPointer//一般是嵌入,也可以选择全局+friendpublic
    	: SmartPointer {(
    	&)
    		:ocObjContainer( objc) ,index(objc0) <<"SmartPointer c"<<;
    		{
    			cout } //指针偏移,保证偏移不越界,空指针则通过返回值判断 bool endloperator
    		++
    
    		(
    		) if(++.
    		{
    			. size(index >= oc)a)//源代码有误,应该在这里先自增//再检查越界,否则后面还可能产生越界returnfalse ;
    				if
    				( .[
    			] ==ocNULLa)index//空指针 return false; return
    				true ;//不越界且有东西
    
    			} booloperator ++
    		(
    
    		int )returnoperator++(
    		{
    			) ;//结果相同}booloperator--
    		(
    
    		) if(--<
    		{
    			0 )returnindex false ;if
    				( .[
    			] ==ocNULLa)indexreturn false ;return
    				true ;}
    			
    			bool operator--
    		(
    
    		int )returnoperator--(
    		{
    			) ;}//间接引用,返回一个成员*operator
    		(
    
    		)
    		Objconst if->(. [
    		{
    			] ==ocNULLa)index//唯一的可能意外,空指针 << "there is a Null Pointer in ObjContainer"<< ;
    			{
    				cout ; } return endl.[
    			]
    			
    			; oc//额,实际上可以合并到这一步,空指针也没问题a}indexprivate:&
    		;
    	//用于绑定容器int
    		ObjContainer; oc//容器索引信息 }
    		; indexbeginSP (
    	)//做个小包装,隐藏掉this调用
    
    	SmartPointer returnSmartPointer(*
    	{
    		this );}private:<
    	*
    ;//容器仅容纳指针向量
    	vector}Obj;> aint main
    (void
    
    ) constint=10
    {
    	; [ sz ] ;;
    	Obj oforsz(int
    	ObjContainer oc=
    	0 ;< i ; ++) i . szadd i(&
    	{
    		oc[]);o}i::=.
    	beginSP
    
    	ObjContainer(SmartPointer sp)oc;//生成迭代器//下面这样也可以,只不过非常麻烦//ObjContainer::SmartPointer sp = ObjContainer::SmartPointer::SmartPointer(oc);dof
    	(
    	)
    	;
    	{
    		sp->g(); //->先左后右,左结合调用operator->,产生Obj*,右就是像一个正常指针
    		sp->}while(++
    	); returnsp0;}
    
    	
  • 初始化一个对象:将取右边的参数(只能有一个),去调用对应的C函数或者CC函数。
  • 赋值一个已经初始化的对象:调用operator=,默认的是位拷贝,可以自定义。
  • # include

    ->*目前感觉没啥用,所以就暂时不写了。

    不重载. 号,因为影响太大了,妨碍了正常使用。

    operator= 与赋值拷贝

    =号的行为有两种

      #include
    #include
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; class
    Fi public std:
    
    Fi ( {
    )}
    	};class {Fee
    public:
    Fee ( {
    int)
    	<<"int"<<; {
    		cout } Fee ( endlint
    	,
    	float)<<"float" <<; {
    		cout } Fee ( endlconst
    	&
    	)<<"cc" Fi<<; {
    		cout } } ; endlint
    	main
    (void
    
    ) =1;//初始化调用C/CC函数
    {
    	Fee fee1 ; =; int
    	Fi fi=
    	Fee fee2 1 fi;
    	float a = 2;
    	// Fee fee3 = a, b; //可以看到,=初始化只能有一个参数 b return 0;
    	}
    
    	# include#
    include
    
    #include
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; class
    Value public std:
    
    Value ( {
    int,
    	int,float aa) : bba ( cc) ,b(aa), c(bb)<< "c"<<cc; 
    	{
    		cout } & //赋值是二元符,其实返回值没那么重要,但是这里返回一个引用,因为后面可能要改 endloperator
    	=
    	Value ( const
    		&)<<"operator=" Value<< rv;
    	{
    		cout = . ; endl=
    		a . rv;a=
    		b . rv;breturn
    		c * rvthisc;
    		} friend&//输出流的本质就是从左向右结合,传递,每次结合一个仍然是ostream类
    	operator
    	<< ostream( &
    		,const&ostream) osreturn << Value"a = " rv<<
    	{
    		
    		. os << " b = " << rv.a << " c = " << rv.b << ; } rvprivatec : endlint
    	,
    ;float
    	; a} b;
    	int cmain
    (void
    
    ) a(0,
    {
    	Value 1,2) ,b (1, 2,3) ;<< "a "<<<<
    	cout ; << "b " a << endl<<
    	cout ; = ; b << endl"a "
    	a << b<<
    	cout ; return 0 a ; endl}
    
    	
  • 复杂带指针对象的赋值。这个要求把指针指向的东西进行递归复制与赋值
  • 引用计数:优化operator=和CC函数的行为。赋值和拷贝构造时不新建空间,而是指向同一个空间,当计数为0,就销毁(有java那味儿了)。要修改对象时,如果有多个引用,那么就执行写拷贝,复制一份新的再写。
  • 通过CC函数和operator=实现类之间的自动类型转换。
  • 开辟内存,一般是调用malloc
  • 常用的三种用法

    1. 调用构造函数
    2. 检查内存分配
    3. 调用析构函数。如果析构函数里有别的delete,可以实现递归调用,清楚所有相关对象
    PStash——new和delete的动态内存管理 new/delete概览

    相当于加强版的malloc类/free类函数,将这些步骤封装起来。而且,可以重载,但是重载往往是用来优化加速的,一般使用默认的即可。

    new和delete让动态内存管理更加方便,从此数组是路人,二级指针闯天下。

    new:

    1. 释放内存,一般是调用free
    2. 建议在delete p后加上p=NULL补刀,防止二次delete同一片区域
    3. delete void*不建议这么做,因为不会调用析构函数,极易引起内存泄漏

    delete:

      #include#include
    PStash
    #include
    // 系统库,System调用相当于cmd#
    include//c++级别字符串 #
    include//c++级别数组 #
    include//文件读写 #
    include//调试 using
    namespace; //注意,我们这里还是使用了void*,需要写好析构函数
    # ifndef stdPSTASH_H
    
    #
    definePSTASH_H class
    PStashpublic :
    
    PStash ( {
    ):
    	quantity(0 ),storage(0) ,next(0) <<"c"<<;
    	{
    		cout } ~ PStash endl(
    	)
    	;//因为有循环,不内联intadd( void
    	* )constint= element10
    	{
    		; if inflateSize ( )//检查扩容
    		inflate (next >= quantity);
    		{
    			}[inflateSize++]
    		=
    		storage;next//指向传入的元素return ( element-1
    		) ;next //返回下标 }void*operator
    	[
    	]( int)constif( index< 0
    	{
    		|| )index return NULL ; index >= next}
    		{
    			return []
    		;
    
    		} storagevoidindex*remove
    	(
    
    	int) void*= indexoperator
    	{
    		[] v ( );if(index!=NULL
    
    		) //确定有东西,在storage中置零指针v [ ]= NULL
    		{
    			storage;index//注意!这里没有清理对象,而是将清理任务扔给了客户 } return;}
    		int
    
    		count v(
    	)
    
    	const return;} private
    	{
    		: nextint
    	;
    //上限int
    	; quantity//下一个索引,也是当前的数量值void
    	* next*;
    
    	//storage实际上是指针数组,只不过不限制大小了voidinflate storage(int
    	) constint= increasesizeof
    	{
    		( void psz * );void**=
    
    		newvoid* new_s [ + ]; memsetquantity ( increase,0
    		,(new_s+ )* )quantity ; increasememcpy ( psz,,
    		*)new_s; storage+= quantity ; pszdelete[
    		quantity ] increase;
    		=;} storage}
    
    		storage ; new_sPStash
    	::
    ~PStash
    
    ()for(int=
    {
    	0 ;< i ; ++) i if next( i[]
    	{
    		!= NULLstorage)i<< "PStash not cleaned up" <<;
    		{
    			cout } } delete endl[
    		]
    	;
    	//对storage指针数组中每一个指针调用delete}# storageendif//PSTASH_H//
    int
    
    main( )
    
    ; for(int
    {
    	PStash intStash=
    
    	0 ;< i 5 ;++ i ) .add i(new
    	{
    		intStashint()) ;//内建类型也可以使用new,快速生成空间和指针i}for(int
    	=
    
    	0 ;< i . count( i ) intStash;++)//要强制转换类型还是很麻烦,后面模板可以解决<< i"intStash["<<
    	{
    		<<
    		cout "] = " << * i ( int * )[]<<;intStash}ifor ( endlint
    	=
    
    	0 ;< i . count( i ) intStash;++)delete( iint*
    	{
    		) .remove()intStash;//remove去掉指针,delete将指针指向的内存释放//int*其实不用加,加了保险i}in(
    		"input.txt"
    	)
    
    	ifstream ;;;while(
    	
    	PStash stringStashgetline
    	string line(
    	, )).inadd line(new
    	{
    		stringStashstring()) ;}linefor(int
    	=
    
    	0 ;< i . count( i ) stringStash;++)<<"stringStash[" i<<<<
    	{
    		cout "] = " << * i ( * ) []string<<;stringStash}ifor ( endlint
    	=
    
    	0 ;< i . count( i ) stringStash;++)delete( i*)
    	{
    		. removestring()stringStash;}returni0;
    	}
    
    	 
    
    
    自己写

    欢迎分享,转载请注明来源:内存溢出

    原文地址: http://outofmemory.cn/langs/676455.html

    (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    上一篇 2022-04-19
    下一篇 2022-04-19

    发表评论

    登录后才能评论

    评论列表(0条)

    保存