话说昨天因为校园网的问题导致现在才发博文~唉,想吐槽~

这个是昨天写的,觉得,用来回顾还是很不错的,比较具体的都在笔记中,尤其我觉得里面经验性的东西还是不错的。

2013-8-26

今天在回顾我以前写的笔记,笔记时间应该是在大二。话说,现在我在实验室一边听着胡彦斌的《葬英雄》一边写着学习笔记~

看了以往的笔记,感觉,以前的字写的确实不怎么样,现在嘛,嘿嘿,也不怎么样。不过,感觉还是很不一样的。想想当初学习C++的辛苦,在看着现在写出来的程序,觉得自己还是进步了不少的。C++这门语言,应该算是最难学的语言了吧,我觉得汇编是比C++简单些的。

还有一点,对于一门语言是否精通,我觉得有个很简单的标准,简单的有点苛刻,就是,能否独立的制作这门语言的编译器。如果连编译器都做出来了,那绝对是精通的。你应该会认同吧?

回到正题。

之前在写一些模板类和函数的时候,经常会遇到因为函数参数使用了引用类型而导致不能传递常量参数的情况。这种情况,我当时还是觉得有点无解的。毕竟C++中引用的本质是常指针,也就是:type * const typename;作为指针,肯定是要指向某个变量的地址的,使用空指针和未初始化的指针要命的。但是今天看了我两年前的笔记,突然发现,C++对于常引用提供了很好的解决方法,就是声明一个const的引用参数,形式如下:

 int testfunction(const int & n)

 {

     return n;

 }

对于这样的函数,给n传递一个常数,比如10,是能够正常使用的。其原理,我没搞懂,一般来说,汇编指令中的常数一般是夹杂在指令码中的,而引用的本质是指针,唉,哪位大神明白其原理,还望指导一下。

另外其实有个事情我上个学期的时候意识到的一点就是,当一个函数参数使用了const声明的时候,是有多一重好处的,就是,能够同时允许使用常量以及变量。比如,字符串类的构造函数其参数就是这样的,声明为const类型后,就能够使用字符串常量以及指向字符串的指针,好用得很。当然了,const最本质的限制就是,不能对传递来的数据进行更改,但是这里我记着存在两种逻辑,EffectiveC++中讲过的。

刚才上面提到了使用我之前对于CString类制作的尝试,在这里我刚才想了半天,只记得是和C++的const成员函数相关,但是具体是什么忘记了。刚才在书翻了半天,找到了const成员函数这一块,一看,就想起来了。其实const成员函数,我个人觉得最主要的用途之一,其实是为类的const类型对象提供相应的方法。

书中给了这样的一个例子,简单的两行代码。

        onst Stock land = Stock(“balabalabala”);

        land.show();

如果show函数不是cosnt成员数,编译器将拒绝(这词用的好)第二行代码,原因就是,show函数无法保证land中的值不会被修改。一般函数(非成员函数)是通过参数来保证的,而很显然,show函数无参数,无法保证。

这里想要调用show函数唯一的方法就是在类的声明中保证show函数不会修改参数,也就是将其声明为const类型的成员函数。就这样。

同时在我当时的代码的相关函数,比如复制构造函数也因此而导致了问题,因为赋值构造函数的参数是const类型的,因此导致当时未声明为const类型的函数在其中出现违规调用的情况,哪怕函数的功能其仅仅是返回一个字符串首地址。对我来说,这个教训还是很深刻的。后来解决方案也很简单,就是,将相关的函数声明为const类型,problem solved。

C++这门语言要注意的细节还真的是不少的。

另外关于const类型,还有一点要谈的,就是我在上个学期的一个小尝试,比较有趣,之前的博客园文章中貌似也发表过的,今天再长谈一番。

这个尝试就是,去修改一个类型为const的整数的值。当时出现这个念头,很大程度上是因为当时我对于数据存储的理解,也就是,内存中的数据用来表示什么,完全决定于你把它当成什么。

为了稍微体现一下我的这个观点,我在这先写一小段代码,展示下结果。

代码如下:

 int num_str = 0x00313233;

 cout << hex << num_str << endl;

 cout << (char *)&num_str << endl;

输出结果:

就这么一段很简单的代码,输出结果却完全不一样,尽管,源自同一片内存

另外上面这段代码涉及大端小端的问题,有兴趣请留言啊。

接着往下说,就是如何修改一个const常量的值。这个是因为C/C++中,const常量是有内存去存储的,而非宏之类的单纯替换。这个方法和上面代码的方法是一致的,通过类型转换,将其改变为你想要的类型来进行翻译和使用。看着这段代码:

 const int constval = ;

 int * pnval = (int *)&constval;

 *pnval = ;

 cout << constval << endl;

现在看这段代码,觉得蛮有几分调皮的意思。而且这里的值和以前的那篇文章貌似都是一样的。

这段代码你运行后,一般的结果依旧是10,原因的话,我当时看了一下汇编代码,应该是编译器优化,最后一行的constval的值被直接用常数10给取代了。但是如果你以调试的模式来查看,就会发现,在第三行代码运行后,cosntval的值被改为了9.就是这样的。

现在有截图了,看运行截图:

现在,我们换个类型,改为结构体,来使编译器放弃这种优化,代码如下:

 struct teststruct

 {

     int val;

 };

 const teststruct constval = {};

 teststruct * pnval = (teststruct *)&constval;

 pnval->val = ;

 cout << constval.val << endl;

运行结果:

代码整体上是差不多的,但是在这里,结果如预期的那样,确实被修改了。

所以在这里,对于什么const也好,结构也好,指针也好,其存储本质都是内存中以字节为单位排列的二进制数,其意义,不同的解释方式会有不同的结果。

你当它是什么,他就是什么。

今天上午的时候不小心把流量超了,下午去买了张校园网充值卡,结果,到现在还是上不了网……同志,我不知道你看到这文章会是什么时候……

闲着也是闲着,室友们打逆战,当时我就比较感慨这种联网游戏是怎么实现的~唉,腾讯还是挺NB的。我上不了网,所以干脆把数据结构之二叉树又写一遍,写数据结构又不需要上网~

这次写二叉树,没用模板,但是改的话,还是比较快的。这个大家都懂得,我用char类型的做在是因为其输入方便。

二叉树的建立本次采用的是用先序的方式,#表示下一节点为空,常规的字符为节点数据。相信熟悉二叉树的同学和前辈们都了解的~

先看节点类和二叉树类的声明:

 class BinaryNode

 {

     //定义为class比较好,有利于函数扩充

     public:

         BinaryNode();

         char m_val;//暂定为字符,代码简单

         BinaryNode * pLeftNode;

         BinaryNode * pRightNode;

 };

 BinaryNode::BinaryNode():m_val(), pLeftNode(NULL), pRightNode(NULL)

 {

     ;

 }

 class CBinaryTree

 {

     private:

         BinaryNode m_Root;

         void Method(BinaryNode * & pNode);//递归函数

         void Del(BinaryNode * & pNode);

         void FS(BinaryNode * pNode);

         void MS(BinaryNode * pNode);

         void BS(BinaryNode * pNode);

     public:

         CBinaryTree();

         ~CBinaryTree();

         void InputTree();

         void Search(int nMode = );

 };

节点类其实就是个带初始化的结构体,没太多说的,常规套路。二叉树类主要两个方法,一个是树的创建,即InputTree,另外一个是Search方法,这个函数整合了三种遍历方式,即前 中 后,换个顺序而已,简单得很。

在二叉树类中,有五个内建方法,Method,用于二叉树的递归构造,Del用于二叉树的清除工作,同样使用递归,而下面的三个函数分别是前 中 后三种方法的递归调用函数。

可以注意到,Method函数以及Del函数的参数都是指针的引用,也就是说需要对指针的值进行修改。其实我刚才向类想Del方法虽然涉及删除,但是不使用指针引用貌似也完全可以。而Method函数则是必须使用指针引用的。因为每个递归函数,只负责本节点的数据处理,而单节点的来源只能来自于参数。这里的思想,我觉得有点类似于发派任务,函数调用后,只负责本节点的数据工作,对于子节点处理,仅有下级的递归函数处理。所以可以看到我的Method函数非常简单。

另外我的这段代码中,第一次的任务是由非递归函数处理的,算是,使用递归函数引导函数吧,通过引导函数,完成第一次的较为特殊的处理后,再由递归函数完成后面的重复性操作。

当然,如果我类中参数定义为指针而不是一个结构体对象,那么直接使用递归函数开始处理也是完全可以的。

还是上不了外网,连QQ截图都没法用,所以,我又把树的分层遍历写了~

刚才看室友们玩逆战的塔防局,他们有个高手带着,一晚上连续通关两次,不过这也是他们从上个学期开始玩逆战到现在唯二的两次通管局,以往都是最后一关失败。但是现在有大腿抱着就是不一样啊。

刚才写的分层遍历代码形式上和前两天发的那个图的遍历思路是一样的,都是通过使用栈来实现分层遍历。不过,我做的这个分层遍历找不到每一层的边界。所以是一起打出来的。但是根据输入能够判断出,确实实现了分层遍历。

贴下树遍历的截图,分别前中后,以及分层遍历。输入是以前序的方式输入的。

最后依旧附上二叉树的全部代码:

 #include <iostream>
#include <queue> using namespace std;
class BinaryNode
{
//定义为class比较好,有利于函数扩充
public:
BinaryNode();
char m_val;//暂定为字符,代码简单
BinaryNode * pLeftNode;
BinaryNode * pRightNode;
};
BinaryNode::BinaryNode():m_val(), pLeftNode(NULL), pRightNode(NULL)
{
;
}
class CBinaryTree
{
private:
BinaryNode m_Root;
void Method(BinaryNode * & pNode);//递归函数
void Del(BinaryNode * & pNode);
void FS(BinaryNode * pNode);
void MS(BinaryNode * pNode);
void BS(BinaryNode * pNode);
public:
CBinaryTree();
~CBinaryTree();
void InputTree();
void Search(int nMode = );
void LevelSearch();//分层遍历
}; CBinaryTree::CBinaryTree()
{
;//nothing
} CBinaryTree::~CBinaryTree()
{
if(m_Root.m_val == )
return;
if(m_Root.pLeftNode != NULL)
Del(m_Root.pLeftNode);
if(m_Root.pLeftNode != NULL)
Del(m_Root.pRightNode);
} void CBinaryTree::InputTree()
{
char val = ;
cin >> val;
if(val == '#')
{
return;
}
else
{
m_Root.m_val = val;
Method(m_Root.pLeftNode);
Method(m_Root.pRightNode);
}
} void CBinaryTree::Del(BinaryNode * & pNode)
{
if(pNode->pLeftNode != NULL)
Del(pNode->pLeftNode);
if(pNode->pRightNode != NULL)
Del(pNode->pRightNode);
delete pNode;
} void CBinaryTree::Method(BinaryNode * & pNode)
{
char val = ;
cin >> val;
if(val != '#')
{
pNode = new BinaryNode;
pNode->m_val = val;
Method(pNode->pLeftNode);
Method(pNode->pRightNode);
}
else
{
pNode = NULL;
}
} void CBinaryTree::FS(BinaryNode * pNode)
{
cout << pNode->m_val;
if(NULL != pNode->pLeftNode)
FS(pNode->pLeftNode);
if(NULL != pNode->pRightNode)
FS(pNode->pRightNode);
} void CBinaryTree::MS(BinaryNode * pNode)
{
if(NULL != pNode->pLeftNode)
MS(pNode->pLeftNode);
cout << pNode->m_val;
if(NULL != pNode->pRightNode)
MS(pNode->pRightNode);
} void CBinaryTree::BS(BinaryNode * pNode)
{
if(NULL != pNode->pLeftNode)
BS(pNode->pLeftNode);
if(NULL != pNode->pRightNode)
BS(pNode->pRightNode);
cout << pNode->m_val;
} void CBinaryTree::Search(int nMode)
{
if( == m_Root.m_val)
{
return;
}
switch(nMode)
{
case :
cout << m_Root.m_val;
if(NULL != m_Root.pLeftNode)
FS(m_Root.pLeftNode);
if(NULL != m_Root.pRightNode)
FS(m_Root.pRightNode);
break;
case :
if(NULL != m_Root.pLeftNode)
MS(m_Root.pLeftNode);
cout << m_Root.m_val;
if(NULL != m_Root.pRightNode)
MS(m_Root.pRightNode);
break;
case :
if(NULL != m_Root.pLeftNode)
BS(m_Root.pLeftNode);
if(NULL != m_Root.pRightNode)
BS(m_Root.pRightNode);
cout << m_Root.m_val;
break;
} } void CBinaryTree::LevelSearch()
{
queue<BinaryNode> qBN;
//cout << m_Root.m_val << endl;
qBN.push(m_Root);
while(!qBN.empty())
{
cout << qBN.front().m_val;
if(NULL != qBN.front().pLeftNode)
qBN.push(*qBN.front().pLeftNode);
if(NULL != qBN.front().pRightNode)
qBN.push(*qBN.front().pRightNode);
qBN.pop();
}
} int main()
{
cout << "Hello world!" << endl;
CBinaryTree tree;
tree.InputTree();
tree.Search();
cout << endl;
tree.Search();
cout << endl;
tree.Search();
cout << endl;
tree.LevelSearch();
return ;
}

C++ const && 二叉树合集的更多相关文章

  1. [题解+总结]NOIP动态规划大合集

    1.前言 NOIP2003-2014动态规划题目大合集,有简单的也有难的(对于我这种动态规划盲当然存在难的),今天就把这些东西归纳一下,做一个比较全面的总结,方便对动态规划有一个更深的理解. 2.NO ...

  2. C# 调用windows api 操作鼠标、键盘、窗体合集...更新中

    鼠标操作window窗体合集...更新中 1.根据句柄查找窗体 引自http://www.2cto.com/kf/201410/343342.html 使用SPY++工具获取窗体   首先打开spy+ ...

  3. dp合集 广场铺砖问题&&硬木地板

    dp合集 广场铺砖问题&&硬木地板 很经典了吧... 前排:思想来自yali朱全民dalao的ppt百度文库免费下载 后排:STO朱全民OTZ 广场铺砖问题 有一个 W 行 H 列的广 ...

  4. 9.15 DP合集水表

    9.15 DP合集水表 显然难了一些啊. 凸多边形的三角剖分 瞄了一眼题解. 和蛤蛤的烦恼一样,裸的区间dp. 设f[i][j]表示i~j的点三角剖分最小代价. 显然\(f[i][i+1]=0,f[i ...

  5. 9.14 DP合集水表

    9.14 DP合集水表 关键子工程 在大型工程的施工前,我们把整个工程划分为若干个子工程,并把这些子工程编号为 1. 2. --. N:这样划分之后,子工程之间就会有一些依赖关系,即一些子工程必须在某 ...

  6. es6常用基础合集

    es6常用基础合集 在实际开发中,ES6已经非常普及了.掌握ES6的知识变成了一种必须.尽管我们在使用时仍然需要经过babel编译. ES6彻底改变了前端的编码风格,可以说对于前端的影响非常巨大.值得 ...

  7. DP+贪心水题合集_C++

    本文含有原创题,涉及版权利益问题,严禁转载,违者追究法律责任 本次是最后一篇免费的考试题解,以后的考试题目以及题解将会以付费的方式阅读,题目质量可以拿本次作为参考 本来半个月前就已经搞得差不多了,然后 ...

  8. 学渣乱搞系列之Tarjan模板合集

    学渣乱搞系列之Tarjan模板合集 by 狂徒归来 一.求强连通子图 #include <iostream> #include <cstdio> #include <cs ...

  9. NOIP动态规划大合集

    1.前言 NOIP2003-2014动态规划题目大合集,有简单的也有难的(对于我这种动态规划盲当然存在难的),今天就把这些东西归纳一下,做一个比较全面的总结,方便对动态规划有一个更深的理解. 2.NO ...

随机推荐

  1. 【Search Insert Position 】cpp

    题目: Given a sorted array and a target value, return the index if the target is found. If not, return ...

  2. python-根据字符串动态生成对象eval

    # -*- coding: utf-8 -*- stock1={ 'stockName':"沈阳机床", ", 'averagePrice_yesterday':34.0 ...

  3. Phpstorm开发记

    Phpsotrm虽然付费项目,但网上有免费的激活码,也可以免费用不是. 1.首先是svn,windows项目下用Phpsotrm需要安装svn时,支付svn命令的,否则会提示找不到svn命令.2.建项 ...

  4. IDEA for Mac 解决控制台乱码问题

    近期发现 idea for mac 版本中 tomcat 控制台有中文的地方出现乱码问题,其实很简单就可以解决. 这里做个笔记,以后可以方便不会的人来解决 ---------------------- ...

  5. USACO 4.1.2 栅栏的木料

    这个讲的超好....一定要看...然后看我代码就好懂啦... http://blog.csdn.net/ta201314/article/details/41287567 各种优化确实非常好....搜 ...

  6. Hibernate中查询数据转成VO对象及注意问题

    大家都可能会遇到,在用json传输数据时,很多字段是来自不同的表数据,需要我们进行封装数据. hibernate提供这么一个方法用来直接封装查询属性: query.setResultTransform ...

  7. GS连接事件

    GS网络连接事件 //网络事件 //这个事件是在libevent里面的收到的事件就是在那个listen里面,就是客户端打开,服务器收到通知 link_stat stat = (link_stat)rP ...

  8. [工作积累] JNI native 函数签名

    对于一个Java 类 class MyClass { ... public native boolean nativeMyFunc(long param); } 一般来说native对应的声明是这样: ...

  9. MySQL杂记

    参考资料: w3school  SQL 教程 : http://www.w3school.com.cn/sql/index.asp 21分钟 MySQL 入门教程 : http://www.cnblo ...

  10. 单件模式(Singleton Pattern)(转)

    概述 Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点.这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考 ...