C++学习笔记2

来源:互联网 发布:手机能删除淘宝评价吗 编辑:程序博客网 时间:2024/06/08 04:38

1. C++ 没有明确定义如何释放指向不是用new 分配的内存地址的指针。下面提供了一些安全的和不安全的delete expressions 表达式。 

 int i;  242 int *pi = &i; string str ="dwarves"; double *pd = new double(33); delete str;           // error: str is not a dynamic object delete pi;            // error: pi refers to a local delete pd;            // ok

2. 零值指针的删除 如果指针的值为0,则在其上做delete 操作是合法的,但这样做没有任何意义:

 int *ip = 0; delete ip;            // ok: always ok to delete a pointer that is equal to 0

C++ 保证:删除0 值的指针是安全的。

 

3. 下列语句哪些(如果有的话)是非法的或错误的?

vector<string> svec22(10); vector<string> *pvec1 = new vector<string>(10); vector<string> *pvec2 = new vector<string>[10];   // Xvector<string> *pv1 = &svec22; vector<string> *pv2 = pvec1;  delete svec22;           // Compile issuedelete pvec1; delete [] pvec2; delete pv1;              // It is not a dynamic objectdelete pv2;              // It has been delete
 

4. 理解算术转换

研究大量例题是帮助理解算术转换的最好方法。下面大部分例题中,要么是将操作数转换为表达式中的最大类型,要么是在赋值表达式中将右操作数转换为左操作数的类型。

 bool flag; char cval; short sval; unsignedshort usval; int ival; unsignedint uival; long lval; unsignedlong ulval; float fval; doubledval; 3.14159L + 'a'; //promote 'a' to int, then convert to long double dval + ival;           // ival converted to double dval + fval;           // fval converted to double ival = dval;           // dval converted (by truncation) toint flag = dval;           // if dval is 0, then flag is false,otherwise true cval + fval;           // cval promoted to int, that intconverted to float sval + cval;           // sval and cval promoted to int cval + lval;           // cval converted to long ival + ulval;          // ival converted to unsigned long usval + ival;          // promotion depends on size of unsignedshort and int uival + lval;          // conversion depends on size ofunsigned int and long

5. 建议:避免使用强制类型转换 强制类型转换关闭或挂起了正常的类型检查。

强烈建议程序员避免使用强制类型转换,不依赖强制类型转换也能写出很好的C++ 程序。

这个建议在如何看待reinterpret_cast 的使用时非常重要。此类强制转换总是非常危险的。

相似地,使用const_cast 也总是预示着设计缺陷。设计合理的系统应不需要使用强制转换抛弃const 特性。

其他的强制转换,如static_cast 和dynamic_cast,各有各的用途,但都不应频繁使用。

每次使用强制转换前,程序员应该仔细考虑是否还有其他不同的方法可以达到同一目的。

如果非强制转换不可,则应限制强制转换值的作用域,并且记录所有假定涉及的类型,这样能减少错误发生的机会。

dynamic_cast:dynamic_cast 支持运行时识别指针或引用所指向的对象。

const_cast ,顾名思义,将转换掉表达式的 const 性质。

static_cast: 编译器隐式执行的任何类型转换都可以由 static_cast 显式完成: 
 double d = 97.0; 
 // cast specified to indicate that the conversion is intentional 
 char ch = static_cast<char>(d); 
当需要将一个较大的算术类型赋值给较小的类型时,使用强制转换非常有用。此时,强制类型转换告诉程序的读者和编译器:我们知道并且不关心潜在的精度损失。对于从一个较大的算术类型到一个较小类型的赋值,编译器通常会产生警告。当我们显式地提供强制类型转换时,警告信息就会被关闭。 

reinterpret_cast:通常为操作数的位模式提供较低层次的重新解释。 
reinterpret_cast 本质上依赖于机器。为了安全地使用 reinterpret_cast,要求程序员完全理解所涉及的数据类型,以及编译器实现强制类型转换的细节。 


6. switch: case 标号必须是整型常量表达式。例如,下面的标号将导致编译时的错误:

 // illegal case labelvalues case 3.14: //noninteger case ival: //nonconstant
 

7. switch 内部的变量定义 :制定这个规则是为避免出现代码跳过变量的定义和初始化的情况。回顾变量的作用域,变量从它的定义点开始有效,直到它所在块结束为止。

// Incorrectcase true:  // error: declaration precedes a case label  string file_name = get_file_name();  break;  case false:  // ... // Correctcase true:  {  // ok: declaration statement within a statement block  277 string file_name = get_file_name();  // ...  }  break;  case false:  // ... 

8. goto 语句提供了函数内部的无条件跳转,实现从goto 语句跳转到同一函数内某个带标号的语句。 

从上世纪60 年代后期开始,不主张使用goto 语句。goto语句使跟踪程序控制流程变得很困难,并且使程序难以理解,也难以修改。所有使用goto 的程序都可以改写为不用goto 语句,因此也就没有必要使用goto 语句了。  

goto 语句的语法规则如下:

 goto label; 

其中label 是用于标识带标号的语句的标识符。在任何语句前提供一个标识符和冒号,即得带标号的语句:

 end: return; //labeled statement, may be target of a goto 

形成标号的标识符只能用作goto 的目标。因为这个原因,标号标识符可以与变量名以及程序里的其他标识符一样,不与别的标识符重名。goto语句和获得所转移的控制权的带标号的语句必须位于于同一个函数内。 goto 语句不能跨越变量的定义语句向前跳转:

 // ... goto end; int ix = 10; //error: goto bypasses declaration statement end: // error: code herecould use ix but the goto bypassed its declaration  293 ix = 42; 如果确实需要在goto 和其跳转对应的标号之间定义变量,则定义必须放在一个块语句中: // ... goto end; // ok:jumps to a point where ix is not defined { int ix = 10; // ... code using ix } end: // ix no longervisible here

 

向后跳过已经执行的变量定义语句则是合法的。为什么?向前跳过未执行的变量定义语句,意味着变量可能在没有定义的情况下使用。向后跳回到一个变量定义之前,则会使系统撤销这个变量,然后再重新创建它:

 // backward jump overdeclaration statement ok begin: int sz = get_size(); if (sz <= 0) { goto begin; }
 

注意:执行goto 语句时,首先撤销变量sz,然后程序的控制流程跳转到带begin: 标号的语句继续执行,再次重新创建和初始化sz 变量。

 

9.使用预处理器进行调试

NDEBUG 预处理变量实现有条件的调试代码:

int main() { #ifndef NDEBUG cerr <<"starting main" << endl; #endif // ...

如果NDEBUG 未定义,那么程序就会将信息写到cerr 中。如果NDEBUG 已经定义了,那么程序执行时将会跳过#ifndef 和#endif 之间的代码。 默认情况下,NDEBUG未定义,这也就意味着必须执行#ifndef 和#endif 之间的代码。在开发程序的过程中,只要保持NDEBUG 未定义就会执行其中的调试语句。开发完成后,要将程序交付给客户时,可通过定义NDEBUG 预处理变量,(有效地)删除这些调试语句。大多数的编译器都提供定义NDEBUG 命令行选项: $ CC -DNDEBUG main.C

预处理器还定义了其余四种在调试时非常有用的常量:

__FILE__ 文件名

__LINE__ 当前行号

__TIME__ 文件被编译的时间

__DATE__ 文件被编译的日期

可使用这些常量在错误消息中提供更多的信息: 

 if (word.size() <threshold) cerr <<"Error: " << _ _FILE_ _ << " :line " << _ _LINE_ _ << endl << "Compiled on " << _ _DATE_ _ << " at" << _ _TIME_ _ << endl << " Wordread was " << word << ":Length too short" << endl;

如果给这个程序提供一个比threshold 短的string 对象,则会产生下面的错误信息:

 Error: wdebug.cc :line 21

 Compiled on Jan 122005 at 19:44:40

 Word read was"foo": Length too short

 

10. 小结

C++ 提供了种类相当有限的语句,其中大多数都会影响程序的控制流:

while、for以及do while 语句,实现反复循环;

if 和switch,提供条件分支结构;

continue,终止当次循环;

break,退出一个循环或switch 语句;

goto,将控制跳转到某个标号语句;


6 0
原创粉丝点击