存档

文章标签 ‘C++’

【细节决定成败】switch篇

2012年4月15日  1,795 views 没有评论

前几天在给埃及局点提供补丁时犯了个低级错误,在switch的一个case中忘了写break语句,造成功能在大部分情况下都是正常的,只有个别场景出现了很诡异的问题,通过函数入参日志还看不出任何问题。

由于switch的各case之间肯定是非常相似的,所以这种问题造成的后果不一定能够立即体现出来,因为执行了下一个case中的逻辑后返回的结果很有可能也是正确的。

这种错误是非常低级的,低级到我都不太好意思写在这里了。但是这种错误又感觉很容易发生,这已经不是我第一次犯这种错误了。之前在做PMS插件时也发生过一次,那次也是功能在部分情况下很正常,只在一种很特殊的场景下会有问题。上次也是定位了很久才发现是个如此低级的问题。已经在同一个地方跌倒了两次,那么绝对不能再有第三次……

今天稍微总结了一下犯这种错误的原因,主要分成两个场景:

1、新写一个switch语句,在写完一个case后直接开始逻辑处理,如果这个case下的逻辑比较复杂,很有可能在写完逻辑后就忘了写break语句了。

2、修改原有的switch语句,有时候修改某个case语句,直接把此case下的所有语句都干掉重写,这时候很容易把break语句一起干掉了。然后在写新的逻辑时就很有可能只关注新的逻辑而遗漏了这个break语句。

之前犯错的两次分别就是以上两种场景,说白了就是只关注case下的逻辑而忽略了switch语句的本身。现在我能想到的预防措施就是要向括号配对一样检查case与break的配对情况,每写一个case语句后首先把break加上。

与此类似的还有if else,有时候也会存在if下逻辑复杂,写完后忘记else分支的情况。为预防此种情况,在写完if语句后必须将else语句先写下来,哪怕else下是个空语句。

分类: 心得笔记 标签: , , ,

【笔记迁移】C++Primer笔记 2010/9/11

2012年2月26日  1,925 views 没有评论

15章 面向对象编程


引用和指针的静态类型(在编译时可知的引用类型或指针类型)与动态类型(指针或引用所绑定的对象的类型,这是仅在运行时可知的)可以不同,这是C++用以支持多态性的基石。


使用作用域限定符可以覆盖虚函数机制并强制函数调用使用虚函数的特定版本。
示例:

Item_base *baseP = &child;
double d = baseP ->Item_base::net_price(42);

这段代码将强制调用Item_base中定义的net_price版本,不会随baseP指针实际指向的对象而变化。
派生类虚函数调用基类版本时,必须显式使用作用于限定符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将会是一个自身调用,从而导致无穷递归。
虚函数


虚函数的默认实参的值由调用该函数的类型定义,与对象的动态类型无关。 在同一虚函数的基类版本和派生类版本中使用不同的默认实参几乎一定会引起麻烦。如果通过基类的引用或指针调用虚函数,但实际执行的是派生类中定义的版本(引用或指针所指对象实际为派生类对象),这时就可能会出现问题,此时,为虚函数的基类版本定义的默认实参将传递给派生类定义的版本,
而派生类版本是用不同的默认实参定义的。

虚函数, 默认实参


所有的基类与派生类的关系应该遵循 派生类应反映与基类的“是一种(is a)”关系。
继承


派生类可以恢复继承成员的访问级别。例如类A private实现继承类B,类B中有Public成员size,那么,在类A的public部分中增加using声明即可。
示例:

class A :private B{
public:
using B::size;
}

派生类可以恢复继承成员的访问级别,但是不能使访问级别比基类中原来指定的更严格或更宽松。
使用class保留字派生时默认具有private继承,使用struct时默认具有public继承,但是使用默认继承是非常罕见的,不论何时都应显式指明继承的派生保护级别。
继承, 访问级别


继承与静态成员:如果基类定义了static成员,则整个继承层次中只有这一个这样的成员。无论从基类派生出了多少个派生类,所有这些类在访问static成员时所访问的是同一个成员。
继承, static


基类与派生类的转换:派生类可以转换为基类但仅仅局限于引用与指针还有用派生类对象对基类对象进行初始化,初始化时派生类的自有部分被忽略。
从基类到派生类没有自动的转换,就算是基类的指针指向派生类对象,当用这个指针给派生类指针赋值时也会出现编译错误,因为编译器无法只掉动态的绑定是否安全,只能通过静态类型来盘判断操作是否安全,如果此操作真的安全,那么可以使用static_cast强制编译器进行转换,或者使用dynamic_cast申请在运行时检查。

继承, 转换


分类: 心得笔记 标签: , , ,

【笔记迁移】C++Primer笔记 2010/9/9

2012年2月26日  2,364 views 2 条评论

Google的NoteBook这回是真的要没了,以前上学时在NoteBook上做了一些笔记,现在都已经被直接导入到Google Docs里了,因为Google Docs不是那么好访问,现在就将之前的这些笔记都迁移到这里来,现在再看这些笔记,有些自己都有点看不懂了。若有时间的话应该再整理一下。

参加工作半年多了,自从去年9月份开始就没有再更新过这个博客了,若不是前几天域名商发邮件提醒我续费,没准已经将这个博客给遗忘了……唉,生活是艰苦的,工作是忙碌的,无限怀念学生时代啊。

14章重载操作符与转换


1、自增自减操作符重载

自增自减的前缀式与后缀式的区分靠的是参数表中的int参数,这个形参是无用的,它唯一的目的是使后缀函数与前缀函数区分开来。后缀式写这个参数,前缀式没有。
自增自减操作符的后缀式重载时,应与内置操作符一致,返回旧值(即尚未自增或自减的值),并且返回值而不是引用。
示例:

前缀式 ClassType& operator++();

后缀式 ClassType operator++(int); // 一般情况下,后缀式用前缀式实现。


2、调用操作符重载()

定义了调用操作符的类,其对象常称为函数对象(Function Object),即它们是行为类似函数的对象。

struct absInt{
int operator() (int val){
return val<0?-val:val;
}
};
int i=42;
absInt absObj;
unsigned int ui = absObj(i);

3、标准库函数对象调用

标准库函数对象使用时要包含functional头文件,有算术函数对象类型、关系函数对象类型、逻辑函数对象类象,就是各种操作符的函数对象,例如plus<Type> 对应+。
用法:函数对象通常用于覆盖算法使用的默认操作符。如sort默认使用operator<按升序对容器进行排序。
如果要降序排列则可以传递函数对象greater。 示例:

sort(svec.begin(),svec.end(),greater<string>());

标准库函数对象


4、函数对象的函数适配器

函数对象的函数适配器有两类

  • 绑定器(binder),它通过讲一个操作数绑定到给定值而将二元函数对象转换为一元函数对象。有bind1st和bind2nd两个,bind1st将给定值绑定到二元函数对象的第一个实参,bind2nd绑定到第二个实参。
  • 求反器(negator),它将谓词函数对象的真值求反。有not1和not2两个,not1将一元函数对象的真值求反,not2将二元函数对象的真值求反。

示例:

//计算容器vec中所有小于或等于10的元素的个数。bind2nd后面等于 <=10,将二元函数对象变为一元。
count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10) );

//计算容器vec中所有不是小于等于10的元素,即大于10的元素,not1将bind2nd(less_equal<int>(), 10)的返回值求反。
count_if(vec.begin(), vec.end(), not1(bind2nd(less_equal<int>(), 10) );

5、转换操作符

转换操作符(conversion operator)是一种特殊的类成员函数。它定义将类类型值转变为其他类型值的转换。转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型。

通用形式:operator type();//没有返回值类型但是有返回值
这里,type表示内置类型名、类类型名或有类型别名所定义的名字。对任何可作为函数返回类型的类型除void外都可以定义转换函数。
转换函数必须是成员函数,不能指定返回值类型,并且形参表必须为空。
类类型转换之后不能再跟另一个类类型转换。如果需要多个类类型转换,则代码将出错。
转换操作符


分类: 心得笔记 标签: , , ,

Python和C++中洗牌算法Shuffle的实现

2011年6月24日  10,097 views 3 条评论

洗牌,就是将有序的集合中的元素以随机的顺序重新排列。

今天本来是想复习一下几种排序算法的……因为没有随机样本,所以才想着要弄洗牌算法的。以前从来就没有想到过这个问题,今天是头一次接触到打乱顺序的算法。

本文的洗牌算法参考了fuqcool的文章

首先是Python

python语言非常高级,它的标准库已经实现了洗牌函数,在Random模块下有个Shuffle函数。
这是在Random.py中的函数原型。

def shuffle(self, x, random=None, int=int):
	"""x, random=random.random -> shuffle list x in place; return None.

	Optional arg random is a 0-argument function returning a random
	float in [0.0, 1.0); by default, the standard random.random.
	"""

	if random is None:
		random = self.random
	for i in reversed(xrange(1, len(x))):
		# pick an element in x[:i+1] with which to exchange x[i]
		j = int(random() * (i+1))
		x[i], x[j] = x[j], x[i]

阅读全文…

malloc()和free()的相关知识

2011年4月28日  4,228 views 2 条评论

之前有一篇文章是写全局变量、静态变量、局部变量、静态局部变量以及栈和堆在内存中的存储区别的,最近我又看了一篇关于C中Malloc函数和Free函数对内存操作的,《浅谈C中的malloc和free》,通过这篇文章,我对C的内存申请以及释放有了新的、全面的认识。我想,不仅是C,C++或者C#等其他语言的内存申请释放应该也是使用相近的方法吧。下面我简单总结了一下《浅谈》的主要内容,再加上一点我自己的理解。

之前,我对malloc和free的了解,仅仅是在C中使用这两个函数来申请和释放内存,只知道怎么使用,用malloc申请一段给定大小的内存,返回一个Void* 指针,当使用完这段内存后需要手动调用free来释放这段内存。

一、malloc() 和 free() 的基本概念和用法:

 

1、函数原型及说明:

void *malloc(long NumBytes): 该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。

void free(void *FirstBytes): 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。

2、函数用法:

这两个函数的用法其实很简单,就是用malloc()申请内存,用完这段内存后交给free()来释放着段内存。简单例子:

#include<stdio.h>
int main()
{
	char *Ptr = NULL;
	Ptr = (char*)malloc(100 * sizeof(char)); //Malloc
	if (NULL == Ptr)
	{
		printf("malloc failed");
		return 1;
	}
	gets(Ptr);
	printf("%s",Ptr);
	//code...

	free(Ptr);//Free
	return 0;
}

阅读全文…

全局变量、局部变量、静态全局变量、静态局部变量在内存里的区别以及栈与堆的区别

2011年4月26日  15,601 views 5 条评论

全集变量、局部变量、静态全局变量、静态局部变量在内存中如何存储,有什么区别,栈和堆的区别,这都是C/C++的基础问题。在各种招聘笔试面试中,经常都能够遇到与之相关的问题。前些日子我看了一些与之相关的文章,现在总结一下存放于此。

先分析一下四种变量的区别:

 

一、先由程序的内存分配说起,一个完整的C/C++程序在运行时会占用的内存分为几个部分。

  1. 栈(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  2. 堆(heap) :一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。malloc和new等操作实际上就是在堆中申请内存,对象使用完后要手动释放,否则只能等待程序结束时由系统回收,会产生内存泄漏。
  3. 全局区(静态区)(static):全局变量和静态变量是存储在一起的,初始化过的全局变量和静态变量在同一块区域,未初始化的全局变量和静态变量存放在一块相邻的区域内。此区域由系统在程序结束后释放。
  4. 文字常量区:常量字符串存放于此,在程序结束后由系统释放。字符常量就是像这样的 char* str=”abc”;其中的”abc”。在实际情况中,是会复用的,比如变量a和b都赋值为”abc”则实际上他们指向同一块地址。
  5. 程序代码区:存放函数体的二进制代码。

阅读全文…