c++ 41 分清值语义与对象语义

默认的构造拷贝函数是浅拷贝

A a1;

A a2(a1); //a1 与a2 之间还存在资源的共享  这里不是值语义

一个类实现了深拷贝之后才算是值语义


string a("abc");

string b(a); //这里是深拷贝

对象语义对象生命不容易控制,(容易产生资源泄漏,内存泄漏问题)

基于对象编程  值语义

值语义对象通常以类对象的方式来使用。

面向对象编程  对象语义

对象语义对象通常以指针或者引用方式来使用。

常用的智能指针:

   auto_ptr           所有权独占,不能共享,但是可以转移。

   shared_ptr      所有权共享,内部维护了一个引用计数,+1 或-1, 变为0 的时候,资源释放。

   weak_ptr      弱指针,它通常与shared_ptr 配合使用,解决循环引用的问题。

  scoped_ptr     与auto_ptr 类似,    所有权独占,不能共享,但是不可以转移。

  这些智能指针释放对象。

  

Node n1;

Node n2(n1);

浅拷贝

当n1 销毁的时候,会释放它的子代。

当n2 销毁的时候,会释放它的子代。

一个对象可能被销毁两次,出现运行时错误。

怎么区分某个对象是值语义对象,还是对象语义对象。

值语义的析构是确定的。

计算机模型中,那写类是值语义对象,那些是对象语义对象?

当前的Node 并没有实现拷贝构造函数。默认情况应该是浅拷贝的,不是值语义。

但是实际上使用是以对象语义来使用的。(用到一些指针)

从语法形式来说,是值语义的,可以被拷贝。但是这时,并没有脱离关系,

Node n1;

Node n2(n1);

这里内部实现的是浅拷贝,并没有将子代的资源拷贝过来,仅仅是指针的指向关系。这样当n1 对象销毁的时候,会释放他的子代,当n2 对象销毁的时候,也会释放他的子代,导致一个对象被销毁两次,出现运行时错误。

解决方案 深拷贝 或者 是 Node 类定义成值语义,(这里深拷贝是没有意义的,就好像是一个人,张三 拷贝 成张三2,没有任何意义,不可能存在两个一样的人)

将node 转换成对象语义:

  两种方法:1,对象拷贝禁止。那么这个对象就是对象语义了。2,拷贝之后,依然共享底层资源,对任何一个改变都将改变另一个。

 一个对象禁止拷贝,只需要将拷贝构造函数设置为私有的。并且不给出实现,= 赋值运算符设置为私有的,不给出实现

#ifndef _NODE_H_
#define _NODE_H_
class Noncopyable{
protected:
	Noncopyable(){};
	~Noncopyable(){};
private:
	Noncopyable(const Noncopyable&);
	const Noncopyable& operator=(const Noncopyable&);
};

//把Node 类变为对象语义  private 继承因为,因为Noncopyable 没有任何接口,是实现继承
class Node:private Noncopyable
{
public:
	virtual double Calc() const=0;//纯虚函数
	virtual ~Node(){};
};
class NumberNode:public Node
{
public:
	double Calc() const;
	NumberNode(double number):number_(number){};
private:
	const double number_;//一经过初始化就不会改变
};
class BinaryNode:public Node
{
public:
	BinaryNode(Node* left,Node* right):left_(left),right_(right)
	{
	}
	~BinaryNode();

	//这里没有实现Calc 方法意味着还是抽象类  不懂如何计算


protected:
	Node* const left_;//一经初始化也不会改变  指针不能改变
	Node* const right_;
};
class UnaryNode:public Node
{
public:
	UnaryNode(Node* child):child_(child)
	{
	}
	~UnaryNode();
protected:
	Node* const child_;
};
class AddNode:public BinaryNode
{
	AddNode(Node* left,Node* right):BinaryNode(left,right)
	{
	}
	double Calc() const;
};
class SubNode:public BinaryNode
{
	SubNode(Node* left,Node* right):BinaryNode(left,right)
	{
	}
	double Calc() const;
};
class MultiplyNode:public BinaryNode
{
	MultiplyNode(Node* left,Node* right):BinaryNode(left,right)
	{
	}
	double Calc() const;
};
class DivideNode:public BinaryNode
{
	DivideNode(Node* left,Node* right):BinaryNode(left,right)
	{
	}
	double Calc() const;
};
class UMInusNode:public UnaryNode
{
	UMInusNode(Node* child):UnaryNode(child)
	{
	}
	double Calc() const;
};
#endif

表达式解析:



表达式解析至少两种方案:

    1,逆波兰表示法    栈的方式

    2,递归下降法,  编译原理 语法树  表达式树

    





Scanner 只负责扫描,并且登记当前状态

Parser    根据扫描结果,进行解析,递归下降解析,直到生成一棵树。

程序的大体框架:

main.cpp

#include <iostream>
#include <string>
#include "Scanner.h"
#include "Parser.h"
using namespace std;
int main(void)
{
	do
	{
		cout<<"> ";
		string buf;
		getline(cin,buf);//输入一行表达式,放到buf 中。
		cout<<buf<<endl;//输出
		Scanner scanner(buf);//扫描表达式
		Parser parser(scanner);
		parser.Parse();//解析
		 parser.Calculate();  //解析完毕,计算
	}while(1);
	return 0;
}

Scanner.h

#ifndef _SCANNER_H_
#define _SCANNER_H_
#include <string>
using namespace std;
class Scanner
{
public:
	Scanner(const string& buf);
private:
	const string buf_;
};
#endif

#include "Scanner.h"


Scanner::Scanner(const string& buf):buf_(buf)
{
}

Parser.h

#ifndef _PARSER_H_
#define _PARSER_H_
class Scanner;
class Parser
{
	//这里是引用的方式来使用Scanner 或者指针的方式,没有必要这么快就包含头文件,可以用前项声明。
	//这样的话,头文件会比较小,cpp 中多次包含了,就会使得可执行文件增大
public:
	Parser(Scanner& scanner);
	void Parse();
	double Calculate() const;
private:
	Scanner& scanner_;
};
#endif

parser.cpp

#include "Parser.h"
#include "Scanner.h"
//引用在初始化列表中进行初始化
Parser::Parser(Scanner& scanner):scanner_(scanner)
{

}
void Parser::Parse()
{

}
//加const 和不加const 是可以构成重载的。
double Parser::Calculate() const
{
	return 0.0;
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值