(原) c++ 杂
Declaration of variables
C++ is a strongly-typed language, and requires every variable to be declared with its type before its first use. This informs the compiler the size to reserve in memory for the variable and how to interpret its value. The syntax to declare a new variable in C++ is straightforward: we simply write the type followed by the variable name (i.e., its identifier). For example:
int a;
float mynumber;
These are two valid declarations of variables. The first one declares a variable of type int with the identifier a. The second one declares a variable of type float with the identifier mynumber. Once declared, the variables a and mynumber can be used within the rest of their scope in the program.
If declaring more than one variable of the same type, they can all be declared in a single statement by separating their identifiers with commas. For example:
int a, b, c;
This declares three variables (a, b and c), all of them of type int, and has exactly the same meaning as:
int a;
int b;
int c;
To see what variable declarations look like in action within a program, let's have a look at the entire C++ code of the example about your mental memory proposed at the beginning of this chapter:
// operating with variables
#include<iostream>
usingnamespace std;
int main ()
{
// declaring variables:
int a, b;
int result;
// process:
a = 5;
b = 2;
a = a + 1;
result = a - b;
// print out the result:
cout << result;
// terminate the program:
return 0;
}
you should get
4
Don't be worried if something else than the variable declarations themselves look a bit strange to you. Most of it will be explained in more detail in coming chapters.
Initialization of variables
When the variables in the example above are declared, they have an undetermined value until they are assigned a value for the first time. But it is possible for a variable to have a specific value from the moment it is declared. This is called the initialization of the variable.
In C++, there are three ways to initialize variables. They are all equivalent and are reminiscent of the evolution of the language over the years:
c-like initialization/ constructor initialization/ uniform initialization
1. c-like initialization: consists of appending an equal sign followed by the value to which the variable is initialized:
type identifier = initial_value;
For example, to declare a variable of type int called x and initialize it to a value of zero from the same moment it is declared, we can write:
int x = 0;
2. known as constructor initialization:(introduced by the C++ language), encloses the initial value between parentheses (()):
type identifier (initial_value);
For example:
int x (0);
3. known as uniform initialization :, but using curly braces ({}) instead of parentheses (this was introduced by the revision of the C++ standard, in 2011):
type identifier {initial_value};
For example:
int x {0};
All three ways of initializing variables are valid and equivalent in C++.
// initialization of variables
#include<iostream>
usingnamespace std;
int main ()
{
int a=5; // initial value: 5
int b(3); // initial value: 3
int c{2}; // initial value: 2
int result; // initial value undetermined
a = a + b;
result = a - c;
cout << result;
return 0;
}
you should get
6
The for loop
for (initialization; condition; increase) statement;
The goto statement
goto allows to make an absolute jump to another point in the program. This unconditional jump ignores nesting levels, and does not cause any automatic stack unwinding. Therefore, it is a feature to use with care, and preferably within the same block of statements, especially in the presence of local variables.
The destination point is identified by a label, which is then used as an argument for the goto statement. A label is made of a valid identifier followed by a colon (:).
goto is generally deemed a low-level feature, with no particular use cases in modern higher-level programming paradigms generally used with C++. But, just as an example, here is a version of our countdown loop using goto:
// goto loop example
#include<iostream>
usingnamespace std;
int main ()
{
int n=10;
mylabel: //这里就是goto 的 jump 之后的位置
cout << n << ", ";
n--;
if (n>0) goto mylabel;
cout << "liftoff!\n";
}
you should get
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, liftoff!
Arguments passed by value and by reference
In certain cases, though, it may be useful to access an external variable from within a function. To do that, arguments can be passed by reference, instead of by value. For example, the function duplicate in this code duplicates the value of its three arguments, causing the variables used as arguments to actually be modified by the call:
// passing parameters by reference
#include<iostream>
using namespace std;
void duplicate (int& a, int& b, int& c)
{
a*=2;
b*=2;
c*=2;
}
int main ()
{
int x=1, y=3, z=7;
duplicate (x, y, z);
cout << "x=" << x << ", y=" << y << ", z=" << z;
return 0;
}
you should get
x=2, y=6, z=14
To gain access to its arguments, the function declares its parameters as references. In C++, references are indicated with an ampersand (&) following the parameter type, as in the parameters taken by duplicate in the example above.
When a variable is passed by reference, what is passed is no longer a copy, but the variable itself, the variable identified by the function parameter, becomes somehow associated with the argument passed to the function, and any modification on their corresponding local variables within the function are reflected in the variables passed as arguments in the call.
In fact, a, b, and c become aliases of the arguments passed on the function call (x, y, and z) and any change on a within the function is actually modifying variable x outside the function. Any change on b modifies y, and any change on c modifies z. That is why when, in the example, function duplicate modifies the values of variables a, b, and c, the values of x, y, and z are affected.
If instead of defining duplicate as:
/‘duplɪket/ adj/n. 完全一样的东西
void duplicate (int& a, int& b, int& c) // PASSED BY REFERENCE
Was it to be defined without the ampersand signs as:
/'æmpəsænd/
void duplicate (int a, int b, int c) // PASSED BY VALUE
The variables would not be passed by reference, but by value, creating instead copies of their values. In this case, the output of the program would have been the values of x, y, and z without being modified (i.e., 1, 3, and 7).
Recursivity
Recursivity is the property that functions have to be called by themselves. It is useful for some tasks, such as sorting elements, or calculating the factorial of numbers. For example, in order to obtain the factorial of a number (n!) the mathematical formula would be:
n! = n * (n-1) * (n-2) * (n-3) ... * 1
More concretely, 5! (factorial of 5) would be:
5! = 5 * 4 * 3 * 2 * 1 = 120
And a recursive function to calculate this in C++ could be:
// factorial calculator
#include<iostream>
using namespace std;
long factorial (long a)
{
if (a > 1){
cout<<a<<"*";
return (a * factorial (a-1));
}
else{
cout<<1<<"=";
return 1;}
}
int main ()
{
long number = 9;
cout << number << "! = " << factorial (number);
return 0;
}
you should get
9! = 9*8*7*6*5*4*3*2*1=362880
Notice how in function factorial we included a call to itself, but only if the argument passed was greater than 1, since, otherwise, the function would perform an infinite recursive loop, in which once it arrived to 0, it would continue multiplying by all the negative numbers (probably provoking a stack overflow at some point during runtime)
Overloaded functions
In C++, two different functions can have the same name if their parameters are different; either because they have a different number of parameters, or because any of their parameters are of a different type. For example:
#include<iostream>
usingnamespace std;
int operate (int a, int b)
{
return (a*b);
}
string operate (string a, string b)
{
return (a+b);
}
int main ()
{
int x=5,y=2;
string n="Hello ";
string m="Wang";
cout << operate (x,y) << '\n';
cout << operate (n,m) << '\n';
return 0;
}
you should get
10
Hello Wang
Note that a function cannot be overloaded only by its return type. At least one of its parameters must have a different type.
Function templates
/'tɛmplet/ n. 模板
C++ has the ability to define functions with generic types, known as function templates. Defining a function template follows the same syntax than a regular function, except that it is preceded by the template keyword and a series of template parameters enclosed in angle-brackets <>:
template <template-parameters> function-declaration
The template parameters are a series of parameters separated by commas. These parameters can be generic template types by specifying either the class or typename keyword followed by an identifier. This identifier can then be used in the function declaration as if it was a regular type. For example, a generic sum function could be defined as:
template <classSomeType>
SomeType sum (SomeType a, SomeType b)
{
return a+b;
}
It makes no difference whether the generic type is specified with keyword class or keyword typename in the template argument list (they are 100% synonyms in template declarations).
In the code above, declaring SomeType (a generic type within the template parameters enclosed in angle-brackets) allows SomeType to be used anywhere in the function definition, just as any other type; it can be used as the type for parameters, as return type, or to declare new variables of this type. In all cases, it represents a generic type that will be determined on the moment the template is instantiated.
Instantiating a template is applying the template to create a function using particular types or values for its template parameters. This is done by calling the function template, with the same syntax as calling a regular function, but specifying the template arguments enclosed in angle brackets:
name <template-arguments> (function-arguments)
For example, the sum function template defined above can be called with:
x = sum<int>(10,20);
The function sum<int> is just one of the possible instantiations of function template sum. In this case, by using int as template argument in the call, the compiler automatically instantiates a version of sum where each occurrence of SomeType is replaced by int, as if it was defined as:
int sum (int a, int b)
{
return a+b;
}
Let's see an actual example:
// function template
#include<iostream>
using namespace std;
template <classT>
T sum (T a, T b)
{
T result;
result = a + b;
return result;
}
int main () {
int i=5, j=6, k;
string f="HELLO ", g="WANG", h;
k=sum<int>(i,j);
h=sum<string>(f,g);
cout << k << '\n';
cout << h << '\n';
return 0;
}
In this case, we have used T as the template parameter name, instead of SomeType. It makes no difference, and T is actually a quite common template parameter name for generic types.
In the example above, we used the function template sum twice.
with arguments of type int.
2. with arguments of type string.
The compiler has instantiated and then called each time the appropriate version of the function.
Note also how T is also used to declare a local variable of that (generic) type within sum:
T result;
Templates are a powerful and versatile feature. They can have multiple template parameters, and the function can still use regular non-templated types. For example:
// function templates
#include<iostream>
using namespace std;
template <classT, classU>
bool are_equal (T a, U b)
{
return (a==b);
}
int main ()
{
if (are_equal(10,10.0))
cout << "x and y are equal\n";
else
cout << "x and y are not equal\n";
return 0;
}
you should get
x and y are equal
Note that this example uses automatic template parameter deduction in the call to are_equal:
are_equal(10,10.0)
Is equivalent to:
are_equal<int,double>(10,10.0)
Non-type template arguments
The template parameters can not only include types introduced by class or typename, but can also include expressions of a particular type:
// template arguments
#include<iostream>
using namespace std;
template <classT, intN>
T fixed_multiply (T val)
{
return val * N;
}
int main() {
cout << fixed_multiply<int,2>(10) << '\n';
cout << fixed_multiply<int,3>(10) << '\n';
}
you should get
20
30
The second argument of the fixed_multiply function template is of type int. It just looks like a regular function parameter, and can actually be used just like one.
But there exists a major difference: the value of template parameters is determined on compile-time to generate a different instantiation of the function fixed_multiply, and thus the value of that argument is never passed during runtime: The two calls to fixed_multiply in main essentially call two versions of the function: one that always multiplies by two, and one that always multiplies by three. For that same reason, the second template argument needs to be a constant expression (it cannot be passed a variable).
A namespace is an optionally named scope. You declare names inside a namespace as you would for a class or an enumeration. You can access names declared inside a namespace the same way you access a nested class name by using the scope resolution (::) operator. However namespaces do not have the additional features of classes or enumerations. The primary purpose of the namespace is to add an additional identifier (the name of the namespace) to a name
namespace identifier
{
named_entities
}
Where identifier is any valid identifier and named_entities is the set of variables, types and functions that are included within the namespace. For example:
namespace myNamespace
{
int a, b;
}
In this case, the variables a and b are normal variables declared within a namespace called myNamespace.
These variables can be accessed from within their namespace normally, with their identifier (either a or b), but if accessed from outside the myNamespace namespace they have to be properly qualified with the scope operator ::. For example, to access the previous variables from outside myNamespace they should be qualified like:
myNamespace::a
myNamespace::b
Namespaces are particularly useful to avoid name collisions. For example:
// namespaces
#include<iostream>
usingnamespace std;
namespace foo
{
int value() { return 5; }
}
namespace bar
{
constdouble pi = 3.1416;
double value() { return 2*pi; }
}
int main () {
cout << foo::value() << '\n';
cout << bar::value() << '\n';
cout << bar::pi << '\n';
return 0;
}
you should get
5
6.2832
3.1416
In this case, there are two functions with the same name: value. One is defined within the namespace foo, and the other one in bar. No redefinition errors happen thanks to namespaces. Notice also how pi is accessed in an unqualified manner from within namespace bar (just as pi), while it is again accessed in main, but here it needs to be qualified as second::pi.
Namespaces can be split: Two segments of a code can be declared in the same namespace:
namespace foo { int a; }
namespace bar { int b; }
namespace foo { int c; }
This declares three variables: a and c are in namespace foo, while b is in namespace bar. Namespaces can even extend across different translation units (i.e., across different files of source code).
Namespace aliasing
Existing namespaces can be aliased with new names, with the following syntax:
namespace new_name = current_name;
Pointers and arrays
mypointer = myarray;
注: 这里= 的顺序非常重要
// more pointers
#include<iostream>
usingnamespace std;
int main ()
{
int numbers[5];
int * p;
p = numbers; *p = 10;
p++; *p = 20;
p = &numbers[2]; *p = 30;
p = numbers + 3; *p = 40;
p = numbers; *(p+4) = 50;
for (int n=0; n<5; n++)
cout << numbers[n] << ", ";
return 0;
}
you should get
10, 20, 30, 40, 50,
*p++ // same as *(p++): increment pointer, and dereference unincremented address
*++p // same as *(++p): increment pointer, and dereference incremented address
++*p // same as ++(*p): dereference pointer, and increment the value it points to
(*p)++ // dereference pointer, and post-increment the value it points to
Pointers to pointers
C++ allows the use of pointers that point to pointers, that these, in its turn, point to data (or even to other pointers). The syntax simply requires an asterisk (*) for each level of indirection in the declaration of the pointer:
char a;
char * b;
char ** c;
a = 'z';
b = &a;
c = &b;

c is of type char** and a value of 8092
*c is of type char* and a value of 7230
**c is of type char and a value of 'z'
void pointers
The void type of pointer is a special type of pointer. In C++, void represents the absence of type. Therefore, void pointers are pointers that point to a value that has no type (and thus also an undetermined length and undetermined dereferencing properties).
This gives void pointers a great flexibility, by being able to point to any data type, from an integer value or a float to a string of characters. In exchange, they have a great limitation: the data pointed by them cannot be directly dereferenced (which is logical, since we have no type to dereference to), and for that reason, any address in a void pointer needs to be transformed into some other pointer type that points to a concrete data type before being dereferenced.
One of its possible uses may be to pass generic parameters to a function. For example:
// increaser
#include<iostream>
usingnamespace std;
void increase (void* data, int psize) // void *data 是空指针
{
if ( psize == sizeof(char) )
{ char* pchar; pchar=(char*)data; ++(*pchar); }// 转化成char 指针
elseif (psize == sizeof(int) )
{ int* pint; pint=(int*)data; ++(*pint); } // 转化成int 指针
}
int main ()
{
char a = 'x';
int b = 1602;
increase (&a,sizeof(a));
increase (&b,sizeof(b));
cout << a << ", " << b << '\n';
return 0;
}
sizeof is an operator integrated in the C++ language that returns the size in bytes of its argument. For non-dynamic data types, this value is a constant. Therefore, for example, sizeof(char) is 1, because char is has always a size of one byte.
Invalid pointers and null pointers
In principle, pointers are meant to point to valid addresses,
the address of a variable
the address of an element in an array.
But pointers can actually point to any address, including addresses that do not refer to any valid element. Typical examples of this are uninitialized pointers and pointers to nonexistent elements of an array:
int * p; // uninitialized pointer (local variable)
int myarray[10];
int * q = myarray+20; // element out of bounds
Neither p nor q point to addresses known to contain a value, but none of the above statements causes an error. In C++, pointers are allowed to take any address value, no matter whether there actually is something at that address or not. What can cause an error is to dereference such a pointer (i.e., actually accessing the value they point to). Accessing such a pointer causes undefined behavior, ranging from an error during runtime to accessing some random value.
But, sometimes, a pointer really needs to explicitly point to nowhere, and not just an invalid address. For such cases, there exists a special value that any pointer type can take: the null pointer value.
This value can be expressed in C++ in two ways:
either with an integer value of zero
the nullptr keyword:
null
int * p = 0;
int * q = nullptr;
int * r = NULL; //defined as an alias of some null pointer constant value (such as 0 or nullptr).
注: The difference between the “Null pointer” and “void pointer”
A null pointer is a value that any pointer can take to represent that it is pointing to "nowhere"
while a void pointer is a type of pointer that can point to somewhere without a specific type.
2.1 one refers to the value stored in the pointer
2.2 the other to the type of data it points to.
Pointers to functions
The typical use of this is for passing a function as an argument to another function. Pointers to functions are declared with the same syntax as a regular function declaration, except that the name of the function is enclosed between parentheses () and an asterisk (*) is inserted before the name:
// pointer to functions
#include<iostream>
usingnamespace std;
int addition (int a, int b)
{ return (a+b); }
int subtraction (int a, int b)
{ return (a-b); }
int operation (int x, int y, int (*functocall)(int,int)) //Pointers to functions
{
int g;
g = (*functocall)(x,y);
return (g);
}
int main ()
{
int m,n;
int (*minus)(int,int) = subtraction;
m = operation (7, 5, addition);
n = operation (20, m, minus);
cout <<n;
return 0;
}
you should get
8
Type aliases (typedef / using)
In C++, there are two syntaxes for creating such type aliases:
using the typedef keyword:
typedef existing_type new_type_name ;
typedefcharC;
typedefunsignedintWORD;
typedefchar * pChar;
typedefcharfield [50];
This defines four type aliases: C, WORD, pChar, and field as char, unsigned int, char* and char[50], respectively. Once these aliases are defined, they can be used in any declaration just like any other valid type:
C mychar, anotherchar, *ptc1;
WORD myword;
pChar ptc2;
field name;
2. using new_type_name = existing_type ;
usingC = char;
usingWORD = unsignedint;
usingpChar = char *;
usingfield = char [50];
The only difference being that
typedef has certain limitationsin the realm of templates that using has not.
using is more generic, although typedef has a longer history and is probably more common in existing code.
(原) c++ 杂的更多相关文章
- IOS 杂笔-9 (MD5 加密)
首先是一段对MD5的简介 *出自一位大牛之手* Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护 ...
- 剥开比原看代码12:比原是如何通过/create-account-receiver创建地址的?
作者:freewind 比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchai ...
- 剥开比原看代码08:比原的Dashboard是怎么做出来的?
作者:freewind 比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchai ...
- 正睿OI DAY3 杂题选讲
正睿OI DAY3 杂题选讲 CodeChef MSTONES n个点,可以构造7条直线使得每个点都在直线上,找到一条直线使得上面的点最多 随机化算法,check到答案的概率为\(1/49\) \(n ...
- Atcoder&CodeForces杂题11.7
Preface 又自己开了场CF/Atcoder杂题,比昨天的稍难,题目也更有趣了 昨晚炉石检验血统果然是非洲人... 希望这是给NOIP2018续点rp吧 A.CF1068C-Colored Roo ...
- Codeforces 杂题集 2.0
记录一些没有写在其他随笔中的 Codeforces 杂题, 以 Problemset 题号排序 1326D2 - Prefix-Suffix Palindrome (Hard version) ...
- 贪心/构造/DP 杂题选做Ⅱ
由于换了台电脑,而我的贪心 & 构造能力依然很拉跨,所以决定再开一个坑( 前传: 贪心/构造/DP 杂题选做 u1s1 我预感还有Ⅲ(欸,这不是我在多项式Ⅱ中说过的原话吗) 24. P5912 ...
- 贪心/构造/DP 杂题选做Ⅲ
颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...
- 想写个小说,关于C#的,名字就叫《原Csharp》吧 (第一回 买书未成炁自生 惶惶回屋遇老翁)
以前也有写过一些小说,但是总是写写停停的,因为忙于项目和其他事情,总是耽搁很久(真的是很久)才会继续动两笔,所以我想先在这里以随笔的方式写个关于C#异世界的小故事吧,更新随缘,也稍微能让自己轻松些. ...
随机推荐
- POJ - 2653 - Pick-up sticks 线段与线段相交
判断线段与线段相交 莫名其妙的数据量 #include <iostream> #include <cstdio> #include <vector> #includ ...
- rr
times = gcd(rotdist,length); printf( ;i<times;i++) { t = vec[i]; j = i; ) { k = j+ r ...
- (转)css中通常会用到浮动与清除,也是一个必须掌握的知识点,概念性的东西不多说,下面举几个例子,来说明它的用法:1.文字环绕效果 2.多个div并排显示 3.清除浮动(默认显示)
一.文字环绕效果: html代码如下: 1 <body> 2 3 <style type="text/css"> 4 #big img {float: le ...
- 学习memcached的一个网站
http://www.ibm.com/developerworks/cn/java/j-memcached1/#resources
- Google机器学习笔记(七)TF.Learn 手写文字识别
转载请注明作者:梦里风林 Google Machine Learning Recipes 7 官方中文博客 - 视频地址 Github工程地址 https://github.com/ahangchen ...
- 【测试技术】ant里面mapper的详细用法
ant里面mapper标签是和fileset配合使用的,目的就是把fileset取出的文件名转成指定的样式.其实看懂官方文档后,感觉真心没啥好写的.但是还是写一下把. 1.<mapper typ ...
- sqlserver查询编辑器编辑数据
1.我想编辑这几行的sortid,方式可以直接写sql,但是还有一种更简洁的方法,如下: 2.用这种方式可以直接修改,比较方便. 3.总结:要做一件事情,可能有很多种方法.而且很有可能有简单的方法,如 ...
- 这样就算会了PHP么?-11
PHP中关于类的基本内容练习: <?php class SportObject{ public $name; public $height; public $avirdupois; public ...
- BZOJ 1877 晨跑
http://www.lydsy.com/JudgeOnline/problem.php?id=1877 思路:拆点费用流,答案就是最大流量和最小费用. #include<algorithm&g ...
- 【转】win32,win64编程永恒;语言编程需要注意的64位和32机器的区别
原文网址:http://www.cnblogs.com/kex1n/archive/2010/10/06/1844737.html 一.数据类型特别是int相关的类型在不同位数机器的平台下长度不同.C ...