随笔分类 -  [010_C++基础]

摘要:俄罗斯OOO Program Verification Systems公司用自己的静态源码分析产品PVS-Studio对一些知名的C/C++开源项目,诸如Apache Http Server、Chromium、Clang、CMake、MySQL等的源码进行了分析,找出了100个典型的Bugs。个人觉得这份列表对C/C++ 程序员有一定参考意义。与其说事后用静态工具分析,倒不如在编码时就提高自知自觉,避免这份列表上的错误发生在你的代码中,因此这里将部分摘录一些Bugs(Bug编号这里不连续,为的是对应原文的编号)并做简要说明。原文将这份Bug列表分为了几类,这里也将沿用这个思路。一、数组和字符串 阅读全文
posted @ 2013-04-26 13:12 邵贤军 阅读(882) 评论(0) 推荐(1) 编辑
摘要:本文,我们将介绍一种利用模板匹配来实现类型甄别的技术。首先,类型是数值的抽象,那么我们从最简单的编译期数值甄别开始看,如果你写下了下面这样一段代码: 1 int main() 2 { 3 int i = 10; 4 i++; 5 bool b = i > 0 ? true:false; 6 if(b){ 7 cout<<"TRUE"<<endl; 8 }else{ 9 cout<<"FALSE"<<endl;10 }11 }上面的代码,对于b的值来说,是编译期就能确定的,肯定是true。试想,如果这段 阅读全文
posted @ 2013-01-05 01:36 邵贤军 阅读(2440) 评论(7) 推荐(1) 编辑
摘要:1 #include <iostream> 2 #include <typeinfo> 3 #include <cxxabi.h> 4 using namespace std; 5 int main(int argc,char* argv[]){ 6 int status; 7 char *realname = abi::__cxa_demangle(typeid(0x0FFFFFFF).name(), 0, 0, &status); 8 std::cout << realname << std::endl; 9 free(r 阅读全文
posted @ 2012-12-31 13:08 邵贤军 阅读(386) 评论(0) 推荐(0) 编辑
摘要:本文的主题是构造函数不能是虚函数,首先这不需要你用脑子去记,因为当你写出来虚构造函数时,编译器是能检查出来的。本文的目的是为什么构造函数不能是虚函数。首先,先看一段错误的代码,下面的代码是通不过编译阶段的。1 class A{2 public:3 virtual A(){4 this->value = 0;5 }6 private:7 int value;8 };为什么构造函数不能是虚函数呢?这里你需要知道一个概念,那就是虚函数表vtbl,每一个拥有虚成员函数的类都有一个指向虚函数表的指针。对象通过虚函数表里存储的虚函数地址来调用虚函数。那虚函数表指针是... 阅读全文
posted @ 2012-12-28 22:39 邵贤军 阅读(5065) 评论(1) 推荐(1) 编辑
摘要:目录1. <<Effective C++>>第三版第12条2. 什么是copying函数?3. 编译器自动生成时,复制的情况4. 自己重载时,复制的情况5. 最黑暗的遗漏复制6. 解决基类成员的复制问题7. 最佳解决办法8. 扩展:构造函数调用构造函数9. new 、operator new 和 placement new 区别10. placement new的含义 11. Placement new 存在的理由 12.如何使用placement new<<Effective C++>>第三版第12条复制对象时勿忘其每一部分。即:通常编译器为我 阅读全文
posted @ 2012-12-27 22:24 邵贤军 阅读(1934) 评论(1) 推荐(1) 编辑
摘要:1. placement new的含义 placement new 是重载operator new 的一个标准、全局的版本,它不能够被自定义的版本代替(不像普通版本的operator new 和 operator delete能够被替换)。1 void *operator new( size_t, void *p ) throw() { return p; } placement new的执行忽略了size_t参数,只返还第二个参数。其结果是允许用户把一个对象放到一个特定的地方,达到调用构造函数的效果。和其他普通的new不同的是,它在括号里多了另外一个参数。比如:1 Widget * p =. 阅读全文
posted @ 2012-12-27 15:28 邵贤军 阅读(399) 评论(0) 推荐(0) 编辑
摘要:序言 首先感谢bolow大神的指导,他教会我查看Visual Studio下C++内存布局的方法,不敢独享,特此分享。 1. Visual Studio命令行查看内存布局的方法cl Test.cpp /d1reportSingleClassLayoutBase 比如你新建了一个源文件Test.cpp,里面有一个类Base,那么上面的命令就能查看类Base的内存布局了。当然,这里我假设你会用Visual Studio编译工具cl,如果不会,可以参考博主博客《[C++基础]042_用Visual Studio的命令行编译源码》。看图认识C++内存布局 以下图片所展示的内存布局主要有:单... 阅读全文
posted @ 2012-12-25 00:01 邵贤军 阅读(1477) 评论(5) 推荐(2) 编辑
摘要:首先感谢bolow大神在我的博客[C++基础]028_获取类成员函数的指针里提的问题,跟bolow一起探讨得到了下面的答案。首先看下面的程序: 1 #include <iostream> 2 using namespace std; 3 class A 4 { 5 public: 6 int i; 7 A():i(0){}; 8 int foo(){return i;} 9 };10 11 class B12 {13 public:14 int j,k;15 B():j(1),k(2){}16 int foo(){return k;}17 };1... 阅读全文
posted @ 2012-12-24 23:10 邵贤军 阅读(755) 评论(0) 推荐(0) 编辑
摘要:1. 命令行进入一下目录,输入cl,回车C:\Program Files\Microsoft Visual Studio 9.0\VC\bin>2. 如果见到下图的错误提示 解决办法: 进入C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE,将其中的mspdb80.dll拷贝到C:\Program Files\Microsoft Visual Studio 9.0\VC\bin 如果遇到缺少mspdb100.dll文件,同理找到mspdb100.dll文件拷贝到bin目录就可以了。3. 命令行输入cl有一下提示则说明可以用命令 阅读全文
posted @ 2012-12-24 21:17 邵贤军 阅读(1482) 评论(0) 推荐(0) 编辑
摘要:关于函数的调用规则(调用约定),大多数时候是不需要了解的,但是如果需要跨语言的编程,比如VC写的dll要delphi调用,则需要了解。 microsoft的vc默认的是__cdecl方式,而windows API则是__stdcall,如果用vc开发dll给其他语言用,则应该指定__stdcall方式。堆栈由谁清除这个很重要,如果是要写汇编函数给C调用,一定要小心堆栈的清除工作,如果是__cdecl方式的函数,则函数本身(如果不用汇编写)则不需要关心保存参数的堆栈的清除,但是如果是__stdcall的规则,一定要在函数退出(ret)前恢复堆栈。1.__cdecl 所谓的... 阅读全文
posted @ 2012-12-19 23:28 邵贤军 阅读(502) 评论(0) 推荐(0) 编辑
摘要:1 #include <stdio.h> 2 #include <stdarg.h> 3 4 void WriteFormatted (FILE * stream, const char * format, ...) 5 { 6 va_list args; 7 va_start (args, format); 8 vfprintf (stream, format, args); 9 va_end (args);10 }11 12 int main ()13 {14 FILE * pFile;15 16 pFile = fopen ("myfile.txt&qu 阅读全文
posted @ 2012-12-19 22:58 邵贤军 阅读(451) 评论(0) 推荐(0) 编辑
摘要:概述 今天听了项目组里的C++高手讲C++的异常,受益匪浅。果然,与高手一起才能学习到更多的东西。下面我就把这位高手介绍的C++异常处理分享给园子里的博友们。什么是异常呢? 在编程语言里,按照出现错误的时机来区分,有编译期错误和运行期错误之分。编译期错误大家肯定很熟悉了,当我们build一个程序时,console里出现的那些error提示就是编译期错误。这些错误是在编译期就能被编译器检查出来。运行期错误大家可能不太经常见,因为自己写的程序往往都是在“温室”里运行的,很少看到程序崩溃的情况。运行期错误的种类很多,举个例子,当我们要在堆上申请内存的时候,内存空间不足,或者在创建文件的时候,磁盘空间 阅读全文
posted @ 2012-11-30 23:29 邵贤军 阅读(2165) 评论(11) 推荐(2) 编辑
摘要:先看两段代码。片段1: 1 union test{int b; char a[2];} shit; 2 3 int main(){ 4 5 shit.a[0]=10; 6 shit.a[1]=1; 7 cout<<sizeof(test)<<endl; 8 cout<<shit.b<<endl; 9 10 system("pause");11 return 0;12 }输出:4266请按任意键继续. . .片段2: 1 int main(){ 2 union test{int b; char a[2];} shit; 3 sh 阅读全文
posted @ 2012-11-26 22:06 邵贤军 阅读(406) 评论(0) 推荐(0) 编辑
摘要:1. 現実场景 在很多电影电视剧里,武功最高的往往是太监,所谓“欲练此功,必先自宫”,太监的武功为什么会这么高?我想应该是闲的。如果说面向对象的世界里有父子的话,那么太监这个类应该就是无法生子的类。他们是无法被继承的,不过,他们是可以继承别人的。 2. 技術問題 要实现一个无法被继承的类,Java为我们提供了一个关键字final,但是C++里没有,那作为C++程序员如何写出一个无法被继承的类呢?构造函数和析构函数。 为什么C++的构造函数和析构函数能够实现这点呢?因为C++的类继承里有一个规则,就是子类在实例化的时候,必须先调用父类的构造函数。对象销毁的时候,也必须调用父类的析构函数。... 阅读全文
posted @ 2012-10-30 21:52 邵贤军 阅读(1289) 评论(6) 推荐(0) 编辑
摘要:芝姐学C++了,下午写了一段C++程序来学习C++语法,下面我们来看她写的代码:1 int *ptr = new int[4];2 *(ptr++) = 3;3 cout<<*ptr<<endl;4 5 delete[] ptr;首先,这段程序的输出是错误的,而且不能正确的运行成功,可疑的是,编译是完全没有错误的。下面是运行截图:上面的代码为什么会有错误呢?我们Debug一下,在第一行打上断点:1 int *ptr = new int[4]; ptr:0xcccccccc2 *(ptr++) = 3; ... 阅读全文
posted @ 2012-10-24 00:12 邵贤军 阅读(447) 评论(0) 推荐(0) 编辑
摘要:1. 前言 关于指针函数和函数指针,特别是函数指针,相信很多C/C++ers跟我曾经一样,对它抱有敬畏,认为它是很高深的东西,其实不然。要理解它花不了多少功夫,或许我一句话就能说清楚二者的区别,但是这样也只是在脑子里形成一个概念而已。大学时代,作为一名学生时,我可以一天看完毛概,考八九十分;但是我用了一个星期去看谭浩强的C++教材(尽管现在很多人鄙视这本教材),上机时却仍无从下手,我可以侃侃而谈,熟悉一切概念,但是就是编不出程序。这就是程序员的世界,凡事只有动手才能领悟真谛。不过这也应证了一句千古名句,也是我最喜欢的一句诗“纸上得来终觉浅,绝知此事要躬行”。 本文所有代码编译及运行环境:win 阅读全文
posted @ 2012-10-19 00:10 邵贤军 阅读(8219) 评论(7) 推荐(5) 编辑
摘要:1. 主版本模板类 首先我们来看一段初学者都能看懂,应用了模板的程序: 1 #include <iostream> 2 using namespace std; 3 4 template<class T1, class T2> 5 class A{ 6 public: 7 void function(T1 value1, T2 value2){ 8 cout<<"value1 = "<<value1<<endl; 9 cout<<"value2 = "<<value2&l 阅读全文
posted @ 2012-10-18 00:04 邵贤军 阅读(5281) 评论(6) 推荐(2) 编辑
摘要:1. New的本质当使用关键字new在堆上动态创建一个对象时,它实际上做了三件事: ①获得一块内存空间 ②调用类构造函数 ③ 返回指向地址的正确指针如果创建的是简单类型的变量,第二步就不执行了。下面我们看一段代码: 1 #include <iostream> 2 using namespace std; 3 4 class A { 5 int m_value; 6 public: 7 A(int value) :m_value(value * value){} 8 void Func(){ 9 printf("m_value=%d\n", m_v... 阅读全文
posted @ 2012-10-17 16:56 邵贤军 阅读(4747) 评论(1) 推荐(1) 编辑
摘要:1. strcat——字符串连接 1 #include <iostream> 2 using namespace std; 3 4 int main(){ 5 6 char str[15] = "abcd"; 7 char str1[] = "abc"; 8 strcat(str, str1); 9 cout<<str<<endl;10 11 system("pause");12 return 0;13 }※注意点,第一个字符串数组要足够大,否则会有越界问题。2. strcpy——字符串拷贝 1 # 阅读全文
posted @ 2012-10-17 00:46 邵贤军 阅读(4281) 评论(0) 推荐(0) 编辑
摘要:1. 获取输入的第一个程序 1 #include <iostream> 2 using namespace std; 3 4 int main(){ 5 6 char str[15]; 7 cin>>str; 8 cout<<str<<endl; 9 10 system("pause");11 return 0;12 }上面的程序可以获取用户的输入,但是如果用户输入如下字符串:abcd cdef输出结果不是我们想要的“abcd cdef”,而是“abcd”。这说明cin认为空格是字符串结束标志。另外,如果我们的用户输入如下字符 阅读全文
posted @ 2012-10-17 00:21 邵贤军 阅读(5090) 评论(0) 推荐(0) 编辑