C++ 编程/丢失的位
或为什么定义一个operator void*()强制转换运算符而不是一个operator bool()?
这样做是为了避免我们错误地写出诸如
int foo = std::cin;
或者,更重要的是,
int bah; std::cin << bah; // observe: << instead of >>
这样的语句。然而,它并不完美,因为它允许其他错误,例如
delete std::cin;
尽管幸运的是,此类错误不太可能发生,因为delete在任何情况下都应该谨慎使用。
最先进的做法是,我们应该在内部定义一个私有嵌套类dummy在std::ios中,并返回 dummy 的指向成员函数的指针 - 从而允许从该指针到bool的隐式转换,但不允许许多其他操作。这有时被称为 "安全布尔" 习语,其动机是 C++ 的bool类型具有从int到 int 的隐式转换,这是标准化过程的结果。
该auto关键字以前的行为有所不同,但在 C++ 中,它允许省略变量的类型,并让编译器决定。这对于泛型编程特别有用,在泛型编程中,函数的返回类型可能取决于其参数的类型。因此,我们无需写成这样
int x = 42;
std::vector<double> numbers;
numbers.push_back(1.0);
numbers.push_back(2.0);
for(std::vector<double>::iterator i = numbers.begin();
i != numbers.end(); ++i) {
cout << *i << " ";
}
可以写成这样
auto x = 42; // We can use auto on base types...
std::vector<double> numbers;
numbers.push_back(1.0);
numbers.push_back(2.0);
// But auto is most useful for complicated types.
for(auto i = numbers.begin(); i != numbers.end(); ++i) {
cout << *i << " ";
}
作用域的概念很简单,除非包含过程;然后,就很难跟踪了
// Confusing Scope Program
#include <iostream>
using namespace std;
int i = 5; /* The first version of the variable 'i' is now in scope */
void p(){
int i = -1; /* A ''new'' variable, also called 'i' has come into existence. The 'i' declared above is now out of scope,*/
i = i + 1;
cout << i << ' '; /* so this ''local'' 'i' will print out the value 0.*/
} /* The newest variable 'i' is now out of scope. The first variable, also called 'i', is in scope again now.*/
int main(){
cout << i << ' '; /* The first variable 'i' is still in scope here so a ''5'' will be output here.*/
char ch;
int i = 6; /* A ''new'' variable, also called 'i' has come into existence. The first variable 'i' is now out of scope again,*/
i = i + 1;
p();
cout << i << endl; /* so this line will print out a ''7''.*/
return 0;
} /* End of program: all variables are, of course, now out of scope.*/
第一个变量 'i' 在两个不同的部分被置于作用域之外。因此,重复的语句 cout << i << ' '; 每次写入都表示不同的含义。每次提到的 'i' 都是内存中的不同位置。这就是引言中提到的 `上下文':语句所处的上下文或背景每次都不一样,因此语句每次在不同的地方执行不同的操作。
关于上面的示例,有一些重要的注意事项。该程序只是一个示例,并且非常复杂,它只用于演示作用域的概念,而不是其他任何东西。虽然它说明了作用域的概念,但它并没有有效地说明作用域的用途。
某些变量需要存储整个程序的信息,而其他变量是短期变量,它们只为单个小目的而短暂地存在,然后通过超出作用域而被销毁。在以下程序中,读取一组数字,然后调用一个过程,该过程计算数组中数字的平均值。在过程中,为了遍历数组并依次选择数组中的元素,会创建一个名为 'i' 的变量,用于执行此目的。对比两种类型的变量:数组本身在整个程序中始终处于作用域内,而变量 'i' 仅在代码的一小部分中处于作用域内,以执行其自身的小任务。
// Program Average
#include <iostream>
using namespace std;
float a[10]; /* a is now in scope.*/
int length; /* length is now in scope.*/
float average(){
float result = 0.0; /* result is now in scope.*/
for(int i = 0; i < length; i++){ /* i is now in scope.*/
result += a[i];
} /* i is now out of scope.*/
return result/length;
} /* result is now out of scope.*/
int main(){
length = 0;
cout << "enter a number for each of the next 10 lines" << endl;
while( length != 10 )
{ cin >> a[length++]; }
float av = average(); /* av is now in scope.*/
cout << endl << "average: " << av << endl;
return 0;
} /* All variables now out of scope.*/
在一个过程中,可以开始一个新的作用域级别。实际上,每次写入左花括号 `{' 时都会发生这种情况,并且在写入与其匹配的右花括号 '}' 的地方结束。因此,可以构建任意深度的作用域层,如以下程序所示。以下程序具有四个作用域级别。最内层的作用域被认为是 包含 其周围的作用域,因此我们谈论内层作用域和外层作用域。同样,该程序仅用于说明目的,它没有任何实际价值。
// Complicated Scope Program
#include <iostream>
using namespace std; /* outermost level of scope starts here */
int i;
int main(){ /* next level of scope starts here */
int i;
i = 5;
{ /* next level of scope starts here */
int j,i;
j = 1;
i = 0;
{ /* innermost level of scope of this program starts here */
int k, i;
i = -1;
j = 6;
k = 2;
} /* innermost level of scope of this program ends here */
cout << j << ' ';
} /* next level of scope ends here */
cout << i << endl;
return 0;
} /* next and outermost levels of scope end here */
该程序的输出是 6 5。为了理解原因,我们首先查看 'i' 的更简单情况,并了解为什么为它打印了 5,然后查看 'j' 的更复杂情况,以及为什么为它打印了 6。
在每个新的作用域级别,都会创建一个新的变量 'i'。因此,仅第一次对 'i' 的赋值(其中 'i' 被赋值为 5)会影响这个特定的变量 'i':即被打印的变量。该变量在第一次赋值后不会改变其值,因此最终的语句打印了一个 5。对 'i' 的其他赋值与最终的打印语句无关。
相反,变量 'j' 仅创建一次,因此,即使变量是在外层作用域中声明的,将 `j' 赋值为 6 的赋值也会改变这个唯一的现有变量 'j'。如果程序有一个在另一个作用域级别内的作用域级别,并且在该内层作用域级别没有声明具有相同名称的变量,那么计算机将 `向外查看' 到下一个外层作用域级别。如果那里也没有声明具有该名称的变量,那么它将继续向外查看,直到找到变量的声明。(当然,如果从未找到变量的声明,那么编译器将指示错误,说明变量未声明,因此程序不会被编译。)
上面我们说过,每个左花括号 `{'. 都会开始一个新的作用域级别。然而,不常用裸左花括号:通常,左花括号与if 语句或while 语句或类似语句相关联。我们在上面的程序中添加了这些语句,以创建一个更常见的(但仍然无用)示例程序。该程序可以编译,并且在运行时打印一个 5,但作为一个程序几乎没有价值。
// Complicated Scope Program, variation 1
#include <iostream>
using namespace std; /* outermost level of scope starts here */
int i;
int main(){ /* next level of scope starts here */
int i;
i = 5;
while(i != 5) { /* next level of scope starts here */
int j,i;
j = 1;
i = 0;
switch (i) { /* next level of scope starts here */
int i;
case 1:
if (i != 4) { /* innermost level of scope of this program starts here */
int k, i;
i = -1;
j = 6;
k = 2;
} /* innermost level of scope of this program ends here */
break;
case 2:
j = 5;
break;
} /* next level of scope ends here */
cout << j << ' ';
} /* next level of scope ends here */
cout << i << endl;
return 0;
} /* next and outermost levels of scope end here */
我们在switch 语句中添加了一个额外的作用域级别,因为该语句要求一个左花括号,并且它表明即使在这一点上,也会打开一个新的作用域级别(因为我们能够声明另一个名为 `i' 的变量而不会出现错误)。如您所见,各种控制结构(即while、if 和switch 语句)都在其各自的匹配右花括号处结束,因此与作用域结束的点相同。可以公平地说,每个控制语句都在作用域的某个区域内运行。但请记住,作用域是一个关于变量名称及其定义区域的概念,而不是关于控制结构的概念。
if 或while 语句在其后具有一个开括号并不是必需的,程序 average 上面展示了其while 语句的示例。在这种情况下,不会开始新的作用域级别。是左花括号打开了新的作用域级别,而不是if 或while 语句本身。switch 语句要求在该点处有一个左花括号,但请注意,`i' 开关变量在旧的作用域级别。新的作用域级别在左花括号之后立即出现,一如既往。
在实际应用中,for
循环控制结构的范围作用方式与其他结构类似。然而,允许在 for
语句本身内声明 for
循环变量,如上面program average中的第八行所示。这是一种良好的编程习惯,因为它使程序结构更加清晰。
在本节中的所有程序中,语句随着作用域级别的加深而向右延伸。这被称为缩进,是程序呈现中非常重要的一个特性。它始终明确地显示作用域级别,并清楚地标明语句的结束位置。例如,在上面的程序中,cout << j << ' '; 位于while循环内,而cout << i << endl; 则位于循环之外。循环和作用域级别在同一位置结束:这两个语句之间的右括号表示两者都已结束。
详细介绍 for
控制语句的作用域
[edit | edit source]本小节可能比有用更令人困惑,但为了完整性而提供;您可以随意跳过它。
for
控制语句具有不寻常的作用域,即左圆括号也开始它自己的作用域级别。因此,以下程序是合法的
// Complicated Scope Program, variation 2
#include <iostream>
using namespace std; /* outermost level of scope starts here */
int i;
int main(){ /* next level of scope starts here */
int i;
i = 5;
for( /* next level of scope starts here */
int i = 1;
i<10 && cout << i << ' ';
++i )
{ /* next level of scope starts here */
int i = -1;
cout << i << ' ';
} /* two levels of scope end here*/
cout << i << endl;
return 0;
} /* next and outermost levels of scope end here */
它输出
1 -1 2 -1 3 -1 4 -1 5 -1 6 -1 7 -1 8 -1 9 -1 5
这种 for
语句的特殊特性不是while 语句所共有的。尝试在while语句内声明变量是语法错误,因此while(int i < 22)i++; 会引发语法错误。
这种特殊的范围级别使我们能够在 for
循环本身内声明一个 for
循环变量(例如上面程序中的变量 'i'),而不是必须在包含的作用域级别声明它,从而创建一个更简洁的程序。但这有点特殊。
上面的程序确实显示了一个有趣的特性:为了检查是否打开了新的作用域级别,只需尝试在该级别再次声明一个已存在的变量即可,就像上面示例程序中对变量 'i' 所做的那样,它在每个可能的级别都声明了一个新的变量 'i'。
上面的程序还说明了另一个非常重要的点:计算机编程中有一句谚语,即可以用任何语言编写糟糕的代码。上面的程序在操作方面相当不清楚,并且是糟糕编码的典型例子:它适合说明一个观点,但不适合阅读。所有代码都应尽可能清晰地编写,使程序尽可能清晰,正如其他地方关于程序风格的讨论中所述。
作用域和生命周期
[edit | edit source]变量的作用域应与其生命周期区分开来。在上面名为 'Confusing Scope Program' 的程序中,第一个变量 'i' 在一段时间内不再处于作用域内,但它仍然存在,因此它的生命周期仍在继续,尽管它不在作用域内。在旧的编程语言中,很难构造出作用域和生命周期不同的例子——通常情况下,在这些旧语言中,两者是相同的,因此生命周期等于作用域。不仅很难,而且在它确实发生的一般情况下也没有那么有用。然而,在最近创建的计算机语言(如 C++)中,使用超出作用域但仍然存活的变量的想法得到了广泛应用,并创造了 C++ 的主要区别特征:C++ 类。这是上面程序用类重写的版本
// Program Average rewritten using a class
#include <iostream>
using namespace std;
class StatisticsPackage{
private:
float aa[20]; /* aa scope start*/
int length; /* length scope start*/
public:
float average(){
float result = 0.0; /* result scope start*/
for(int i = 0; i < length; ++i) /* i scope start*/
result += aa[i];
return result/length;
} /* result and i scope end*/
void get_data(){
length = 0;
while(cin >> aa[length++]);
--length;
}
}; /* aa and length scope end*/
int main(){
StatisticsPackage sp; /* aa and length lifetimes start */
sp.get_data();
float av = sp.average(); /* av scope start*/
cout << av << endl;
return 0;
} /* av scope end*/
在这个版本的程序中,变量 'length' 和 'aa' 在类 'sp' 出现后仍然存活。但是,它们的范围已经限制了:它们是存活的,但不在作用域内,存储了信息,但不能在主程序中直接访问。以这种方式将变量保持在作用域之外对调试程序非常有用,因为它缩小了可能更改变量值的行的数量。
strcat_s
[edit | edit source]strcat_s 函数被提议用作 strcat 函数的近似替代品。strcat_s 比strcat 多一个参数,该参数指定目标字符串的最大大小。strcat_s 函数将源字符串的字符追加到第一个字符串目标字符串的末尾
语法 |
#include <string.h>
errno_t strcat_s(char * dest,rsize_t s1max,const char * scr);
|
例如
char str1 , str2;
printf( "Enter your first string : " );
scanf( "%s", str1 );
printf( "Enter your second string: " );
scanf( "%s", str2);
strcat_s(str1, 16, str2);
printf( " %s\n", str1 );