#include
using namespace std;
int main(void)
{
const int Arsize = 20;
char name[Arsize];
char desert[Arsize];
cout << "Enter your name:\n";
cin >> name;
cout << "Enter your favorite desert:\n";
cin >> desert;
cout << "I have some delicious " << desert;
cout << " for you." << name << endl;
system("pause");
return 0;
}
cin使用空白(空格、制表符、换行符)来确定字符串的结束位置。
getline()和get()都读取一行输入,直到达到换行符。随后getline()将丢弃换行符,而get将换行符保留在输入序列中。
1.面向行的输入getline()
语法:cin.getline(数组名称,字符数);
例如,假设要使用getline()将姓名读入到一个包含20个元素的name数组中,cin.getline(name,20);字符数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。
#include
using namespace std;
int main(void)
{
const int Arsize = 20;
char name[Arsize];
char desert[Arsize];
cout << "Enter your name:\n";
//cin >> name;
cin.getline(name, Arsize);
cout << "Enter your favorite desert:\n";
//cin >> desert;
cin.getline(desert, Arsize);
cout << "I have some delicious " << desert;
cout << " for you." << name << endl;
system("pause");
return 0;
}
getline()函数每次读取一行,它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时,用空字符来替换换行符。
2.面向行的输入:get()
get()不丢弃换行符,将其留在输入队列中。假设调用两次get():
cin.get(name,Arsize);
cin.get(desert,Arsize); //a problem
第一次调用后,换行符留在输入队列中,第二次调用看到的第一个字符便是换行符。因此get()认为已达行尾,而没有发现任何可读取的内容。
解决方式:
1.cin.get(name,Arsize);
cin.get();
cin.get(desert,Arsize);
2.cin.get(name,Arsize).get();
cin.get(desert,Arsize).get();
3.空行及其他问题
get()读取空行后将设置失效位,接下来的输入将被阻断,但可以使用cin.clear();恢复输入。
4.混合输入字符串和数字
#include
using namespace std;
int main(void)
{
cout << "What year was your house built?" << endl;
int year;
cin >> year;
cout << "what is its street address?" << endl;
char address[80];
cin.getline(address, 80);
cout << "Year built:" << year << endl;
cout << "address:" << address << endl;
cout << "Done" << endl;
system("pause");
return 0;
}
解决方法:在读取地址之前先读取并丢弃换行符。
1.cin >>year;
cin.get(); //or cin.get(ch);
2.(cin>>year).get(); //or (cin>>year).get(ch);
1.不能将一个数组赋给另一个数组,但可以将一个string对象 赋给另一个string对象。
例如:char charr1[20];char charr2[20] = "jaguar"; charr1 = charr2; //×
string str1; string str2 = "panther"; str1 = str2; //√
2.string类简化了字符串合并操作。可以用运算符+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string对象的末尾。
能够存储不同的数据类型,但只能同时存储一种类型。共用体常用来节省内存。
1.设置枚举变量的值
2.枚举的取值范围
上限:找到枚举量的最大值,找到大于最大值的最小的2的幂,将它减去1,得到上限。如,最大枚举值是101,则在2的幂中,比这个数大的最小值为128,因此取值范围上限为127。
下限:找到枚举量的最小值。如果它大于等于0,下限为0。如果是负数,与寻找上限方式相同,但最后加上负号。例如,最小枚举量为-6,而比它小的、最大的2的幂是-8,因此下限为-7。
1.int *ps = new int; //allocate memory with new
......... //use the memory
delete ps; //free memory
2.释放ps指向的内存,但不会删除ps指针本身,例如,可以将ps重新指向另一个新分配的内存块。
内存泄漏:不配对使用new和delete,一直new而不释放,被分配的内存再也无法使用。
3.不能释放已经释放的内存。
4.不能使用delete释放声明变量所获得的内存,只能用来释放new分配的内存。
静态联编:在编译时给数组分配内存。
动态联编:在运行阶段创建需要的数组并选择长度。动态数组。
1.使用new来创建动态数组
int *psome = new int [10]; //new元素返回第一个元素的地址,该地址被赋值给指针psome。
delete [] psome; //方括号告诉元素释放整个数组,而不仅仅是指针指向的元素。
2.使用new和delete应遵守的规则
3.使用动态数组
访问动态数组:将指针当作数组名即可。int *psome = new int [10];则第一个元素psome[0],第二个元素psome[1]..........
1.将整数变量加1后,其值将增加1。但将指针变量加1后,增加的量等于它所指向类型的字节数。
2.arrayname[i] = *(arrayname + i) pointername[i] = *(pointername + i)
3.数组名和指针名区别:
(1)可以修改指针的值,而数组名是常量。
pointername = pointername+1; //vaild
arrayname = arrayname+1; //not allowed
(2)对数组应用sizeof()运算符得到的是数组的长度,而对指针应用sizeof()得到的是指针的长度。
1.创建结构: inflatable *ps = new inflatable; 将足以存储inflatable结构的一块可用内存的地址赋给ps。
2.访问结构成员:如果标识符是结构名,则使用句点运算符。如果标志符是指向结构的指针,则使用箭头运算符。还有一种方法是,如果ps是指向结构的指针,则*ps就是被指向的值——结构本身,因此(*ps).price是该结构的price成员。
3.一个使用new和delete的示例
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
char* getname(void)
{
char temp[80];
cout << "Enter the last name:";
cin >> temp;
char* pn = new char[strlen(temp) +1];
strcpy(pn, temp); //copy string into smaller space
return pn;
}
int main(void)
{
char* name;
name = getname();
cout << name << " at " << (int*)name << endl;
delete[] name;
name = getname();
cout << name << " at " << (int*)name << endl;
delete[] name;
system("pause");
return 0;
}
getname()函数返回一个指向输入字符串的指针。该函数将输入读入到一个大型的临时数组中,然后使用new创建一个刚好嫩刚好能存储该输入字符串的内存块,并返回一个指向该内存块的指针。(节省内存)
c++3种管理数据内存的方式:自动存储、静态存储、动态存储。
1.自动存储:局部变量 在所属函数被调用时自动产生,在该函数结束时消亡 通常存储在栈中 后进先出 在程序执行过程中栈不断地增大和缩小
2.静态存储:整个程序执行期间都存在 静态变量有两种:一种是在函数外面定义它,另一种是在声明变量时使用关键字stattic。
3.动态存储: 堆区
模板类vector 类似于string类,也是一种动态数组。基本上,他是使用new创建动态数组的替代品。
#include
#include
#include
using namespace std;
int main(void)
{
//数组
double a1[4] = { 1.2,2.4,3.6,4.8 };
//vector对象
vector a2(4);
a2[0] = 1.0 / 3.0;
a2[1] = 1.0 / 5.0;
a2[2] = 1.0 / 7.0;
a2[3] = 1.0 / 9.0;
//array对象
array a3 = { 3.14,2.72,1.62,1.41 };
arraya4;
a4 = a3;
cout << "a1[2]:" << a1[2] << " at " << &a1[2] << endl;
cout << "a2[2]:" << a1[2] << " at " << &a2[2] << endl;
cout << "a3[2]:" << a1[2] << " at " << &a3[2] << endl;
cout << "a4[2]:" << a1[2] << " at " << &a4[2] << endl;
a1[-2] = 20.2;
cout << "a1[-2]:" << a1[-2] << " at " << &a1[-2] << endl;
cout << "a3[2]:" << a3[2] << " at " << &a3[2] << endl;
cout << "a4[2]:" << a4[2] << " at " << &a4[2] << endl;
system("pause");
return 0;
}
总结:
1.通常,cout在显示bool值前将他们转化为int,但cout.setf(ios::boolalpha)函数调用设置了一个标记,该标记命令cout显示true和false,而不是1和0.(p129)
假设要知道字符数组中的字符串是不是mate。如果word是数组名:word=="mate"; ❌ 因为数组名是地址,用引号括起的字符串常量也是其地址。因此这个关系表达式不能判断两个字符串是否相同,而是在查看他们是否存储在相同的地址上。虽然他们包含相同的字符,特们的地址不同。
c风格字符的比较应使用strcmp()函数来比较。该函数接受两个字符串地址作为参数。这意味着参数可以是指针、字符串常量或字符数组名。如果两个字符串相等,该函数将返回0;如果第一个字符串按ASSCI码顺序排在第二个字符串之后,则strcmp()将返回一个正数值。如果第一个字符串按ASSCI码顺序排在第二个字符串之前,则strcmp()将返回一个负数值。
#include
using namespace std;
int main(void)
{
char word[5] = "?ate";
for (char ch = 'a'; strcmp(word, "mate"); ch++)
{
cout << word << endl;
word[0] = ch;
}
cout << "After loop ends,word is " << word << endl;
system("pause");
return 0;
}
#include
using namespace std;
int main(void)
{
string word = "?ate";
for (char ch = 'a'; word != "mate"; ch++)
{
cout << word << endl;
word[0] = ch;
}
cout << "After loop ends,word is " << word << endl;
system("pause");
return 0;
}
关系运算符可以用于string 对象
clock()返回的时间不一定是秒;clock()函数的返回类型在某些系统上可能是long,在另一些系统上是unsigned long类型或其他类型。
头文件ctime。定义了一个符号常量——CLOCKS-PER-SEC,该常量等于每秒钟包含的系统时间单位数。系统时间除以CLOCKS-PER-SEC可以得到秒数。秒数乘以CLOCKS-PER-SEC可以得到以系统时间为单位的时间。ctime()将clock_t作为clock()返回类型的别名。
#include
#include
using namespace std;
int main(void)
{
cout << "Enter tne delay time,in seconds:";
float secs;
cin >> secs;
clock_t delay = secs * CLOCKS_PER_SEC;
cout << "starting\a\n";
clock_t start = clock();
while (clock() - start < delay)
;
cout << "done\a\n";
system("pause");
return 0;
}
cin忽略换行符和空格,因此输入中的空格没有被回显,也没有被包括在计数内。
发送给cin的输入会被缓冲,这意味着只有在用户按下回车键后,他输入的内容才会被发送给程序。因此,在运行该程序时,可以在#后面输入字符。
在windows10,模拟EOF是在一个新行的开头输入ctrl + z
#include
using namespace std;
int main(void)
{
char ch;
int count = 0;
cin.get(ch);
while (cin.fail() == false)//test for EOF
{
cout << ch;
++count;
cin.get(ch);
}
cout << endl << count<< "characters read\n";
system("pause");
return 0;
}
回车也算一个字符。
常见的字符输入做法:
每次读取一个字符,直到遇到EOF的输入循环的基本设计如下:
cin.get(ch); //attempt to read a char
while (cin.fail() == false) //test for EOF
{
... //do stuff
cin.get(ch); //attempt to read another char
}
简化后
while (cin.get(ch)) //while input is sucessful
{
... //do stuff
}
可以使用int ch,并用cin.get()代替cin.get(char),用cout.put()代替cout,用EOF代替cin.fail()测试:
int ch;//for compatibility with EOF value
ch = cin.get();
while (ch != EOF)
{
cout.put(ch);
++count;
ch = cin.get();
}
如果ch是一个字符,则将循环显示它;如果ch为EOF,则循环将结束。
#include
using namespace std;
enum {red,orange,yellow,green,blue,violet,indigo};
int main(void)
{
cout << "Enter color code(0-6):";
int code;
cin >> code;
while (code >= red && code <= indigo)
{
switch (code)
{
case red:
cout << "Her lips were red." << endl;
break;
case orange:
cout << "Her hair was orange." << endl;
break;
case yellow:
cout << "Her shoes were yellow." << endl;
break;
case green:
cout << "Her nails were green." << endl;
break;
case blue:
cout << "Her sweatsuit was blue." << endl;
break;
case violet:
cout << "Her eyes was violet." << endl;
break;
case indigo:
cout << "Her mood was indigo." << endl;
break;
}
cout << "Enter color code(0-6):";
cin >> code;
}
cout << "Bye" << endl;
system("pause");
return 0;
}
#include
using namespace std;
const int Arsize = 80;
int main(void)
{
char line[Arsize];
int spaces = 0;
cout << "Enter a line of text:" << endl;
cin.getline(line, Arsize);
cout << "Complete line:" << line << endl;
cout << "Line through first period:" << endl;
for (int i = 0; line[i] != '\0'; i++)
{
cout << line[i];
if (line[i] == '.')
break;
if (line[i] != ' ')
continue;
spaces++;
}
cout << endl << spaces << " spaces" << endl;
cout << "Done." << endl;
system("pause");
return 0;
}
#include
using namespace std;
const int Max = 5;
int main(void)
{
//get data
double fish[Max];
cout << "Please enter the weights of your fish." << endl;
cout << "You may enter up to " << Max << " fish." << endl;
cout << "fish #1:";
int i = 0;
while (i < Max && cin >> fish[i])
{
if (++i < Max)
cout << "fish #" << i + 1 << ":";
}
//caluate average
double total = 0.0;
for (int j = 0; j < i; j++)
total += fish[j];
//report results
if (i == 0)
cout << "No fish" << endl;
else
cout << total / i << " = average weight of " << i << " fish" << endl;
cout << "Done" << endl;
system("pause");
return 0;
}
注意:
1.如果逻辑AND表达式左侧为false,则c++不会判断右侧的表达式。i < Max && cin >> fish[i]说明如果i = Max,循环将结束,而不会将下一个值读入到数组后面的位置中。
2.当用户输入不是数字时,该程序将不再读取输入。
#include
using namespace std;
const int Max = 5;
int main(void)
{
//get data
double golf[Max];
cout << "Please enter your golf scores." << endl;
cout << "You must enter " << Max << "rounds." << endl;
int i;
for (i = 0; i < Max; i++)
{
cout << "round #" << i + 1 << ":";
while (!(cin >> golf[i]))
{
cin.clear(); //reset input
while (cin.get() != '\n')
continue; //get rid of bad input
cout << "Please enter a number:";
}
}
//calute average
double total = 0.0;
for (i = 0; i < Max;i++)
{
total += golf[i];
}
//report results
cout << total/Max << " = average score " << Max << " rounds." << endl;
system("pause");
return 0;
}
处理错误代码的关键部分
while (!(cin >> golf[i]))
{
cin.clear(); //reset input
while (cin.get() != '\n')
continue; //get rid of bad input
cout << "Please enter a number:";
}
如果省略cin.clear(),程序将拒绝读取输入。
使用文件输出的主要步骤如下:
#include
#include //for file I/O
using namespace std;
//包含头文件fstream.
//创建一个ofstream对象。
//将该ofstream对象同一个文件关联起来。
//就像使用cout那样使用该ofstream对象。
int main(void)
{
char automobile[50];
int year;
double a_price;
double d_price;
ofstream outFile; //creat object for output
outFile.open("carinfo.txt"); //associate with a file
cout << "Enter the make and model of automobile:";
cin.getline(automobile, 50);
cout << "Enter the model year:";
cin >> year;
cout << "Enter the original asking price:";
cin >> a_price;
d_price = 0.913 * a_price;
//display information on screen with cout
cout << fixed; //用一般的方式输出浮点型,例如C++程序在控制台显示的时候大一点的数,
//显示的时候使用了科学计数法,使用该命令即可像一般的方式显示
cout.precision(2); //设置精确度为2,并返回上一次的设置。
cout.setf(ios_base::showpoint); //显示浮点数小数点后面的零。
cout << "Make and model:" << automobile << endl;
cout << "Year: " << year << endl;
cout << "Was asking $" << a_price << endl;
cout << "Now asking $" << d_price << endl;
//now do exact same thing using outFile instead of cout
outFile << fixed;
outFile.precision(2);
outFile.setf(ios_base::showpoint);
outFile << "Make and model:" << automobile << endl;
outFile << "Year: " << year << endl;
outFile << "Was asking $" << a_price << endl;
outFile << "Now asking $" << d_price << endl;
outFile.close(); //done with file
system("pause");
return 0;
}
读取文本文件的主要步骤:
检查文件是否被成功打开:
inFile.open("bolwing.txt");
if (!inFile.is_open())
{
exit(EXIT_FAILURE);
}
程序:打开用户指定的文件,读取其中的数字,然后指出文件中包含多少个值以及他们的和与均值。
#include
#include //file I/O support
#include //support for exit()
using namespace std;
const int SIZE = 60;
int main(void)
{
char filename[SIZE];
ifstream inFile; //object for handing file input
cout << "Enter of data file:" << endl;
cin.getline(filename, SIZE);
inFile.open(filename); //associate inFile with a file
if (!inFile.is_open()) //failed to open file
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating." << endl;
exit(EXIT_FAILURE);
}
double value;
double sum = 0.0;
int count = 0; //number of items read
inFile >> value; //get first value
while (inFile.good())
{
count++; //one more item read
sum += value; //caculate running total
inFile >> value; //get next value
}
if (inFile.eof())
cout << "End of file reached." << endl;
else if (inFile.fail())
cout << "Input terminated for unknown reason." << endl;
else
cout << "Input terminated for unknown reason." << endl;
if (count == 0)
cout << "No data processed." << endl;
else
{
cout << "Item read: " << count << endl;
cout << "Sum: " << sum << endl;
cout << "Average: " << sum / count << endl;
}
inFile.close();
system("pause");
return 0;
}
注意:Windows文本文件的每行都以回车字符(\r)和换行符(\n)结尾;有些文本编辑器(比如测试时用的记事本)不会自动在最后一行末尾加上换行符。所以需要在输入最后的文本按下回车键,然后再保存文件。不然会出错!!!
1.p213 arr[i] == *(arr + i); &arr[i] == arr +i;
将指针(包括数组名)加1,实际上是加了一个与指针指向的类型的长度(以字节为单位)相等的值。
函数无法返回一个字符串,但可以返回字符串的地址,这样做的效率更高。
#include
using namespace std;
char* buildstr(char c, int n);
int main(void)
{
int times;
char ch;
cout << "Enter a character:";
cin >> ch;
cout << "Enter an integer:";
cin >> times;
char* ps = buildstr(ch, times);
cout << ps << endl;
delete []ps;
ps = buildstr('+', 20);
cout << ps << "-DONE-" << ps << endl;
delete[]ps;
system("pause");
return 0;
}
char* buildstr(char c, int n)
{
char* pstr = new char[n + 1];
pstr[n] = '\0';
while (n-- > 0)
{
pstr[n] = c;
}
return pstr;
}
1.引用变量的主要用途:用作函数的形参。
2.通过将引用变量做参数,函数将使用原始数据,而不是其副本。
3.引用也为函数处理大型结构提供了一种方便的途径。
注意:1.必须在声明引用变量时进行初始化。
int &rodents = rats; ✔ int rat; int & rodent; rodent = rat; ❌
2.引用与某个变量关联起来,将一直效忠于它。
1.如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用。
2.临时变量、引用参数和const
(1)如果实参与引用参数不匹配,C++将生成临时变量。当前,仅当参数为const引用时,C++才 允许这样做。
(2)C++在下面两种情况创建临时变量:
a.实参的类型正确,但不是左值;
b.实参的类型不正确,但可以转换为正确的类型。
(变量、数组元素、结构成员、引用和解除引用的指针都是左值)
(3)如果函数调用的参数不是左值或者与相应的const引用参数类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传给该匿名变量,并让参数引用该变量。
如果需要多个将同一种算法用于不同类型的函数,使用函数模板。
#include
//函数原型
template //or class T
void Swap(T& a, T& b);
using namespace std;
int main(void)
{
int i = 10;
int j = 20;
cout << "i,j = " << i << " , " << j << endl;
cout << "Using compiler-generated int swaper:" << endl;
Swap(i, j);
cout << "Now,i,j = " << i << "," << j << endl;
double x = 24.5;
double y = 81.7;
cout << "x,y = " << x << "," << y << endl;
cout << "Using compiler-generated double swaper:" << endl;
Swap(x, y);
cout << "Now,x,y = " << x << "," << y << endl;
system("pause");
return 0;
}
//函数定义
template
void Swap (T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
更常见的情形是,将模板放在头文件中,并在需要使用模板的文件中包含头文件。
#include
//函数原型
template //or class T original template
void Swap(T& a, T& b);
template //new template
void Swap(T* a, T* b, int n);
void Show(int a[]);
const int Lim = 8;
using namespace std;
int main(void)
{
int i = 10;
int j = 20;
cout << "i,j = " << i << " , " << j << endl;
cout << "Using compiler-generated int swaper:" << endl;
Swap(i, j); //matches original template
cout << "Now,i,j = " << i << "," << j << endl;
int d1[Lim] = { 0,7,0,4,1,7,7,6 };
int d2[Lim] = { 0,7,2,0,1,9,6,9 };
cout << "Original arrays:" << endl;
Show(d1);
Show(d2);
Swap(d1, d2, Lim); //matches new template
cout << "Swapped arrays:" << endl;
Show(d1);
Show(d2);
system("pause");
return 0;
}
//函数定义
template
void Swap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template
void Swap(T a[], T b[],int n)
{
T temp;
for (int i = 0; i < n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
void Show(int a[])
{
cout << a[0] << a[1] << "/";
cout << a[2] << a[3] << "/";
for (int i = 4; i < Lim; i++)
cout << a[i];
cout << endl;
}
解决方案:
1.对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及他们的重载版本。
2.显式具体化的原型和定义应该以template<>打头,并通过名称指出类型。
非模板函数:void Swap(job &,job &);
模板函数:template
具体化:template<> void Swap
3.如果有多个原型,编译器在选原型时,非模板函数优先于具体化和常规模板,具体化优先于常规模板。
4.显式具体化示例
#include
//函数原型
template //or class T original template
void Swap(T& a, T& b);
struct job
{
char name[40];
double salary;
int floor;
};
//具体化
template <> void Swap(job& j1, job& j2);
void Show(job& j);
using namespace std;
int main(void)
{
cout.precision(2);
cout.setf(ios::fixed, ios::floatfield);
int i = 10;
int j = 20;
cout << "i,j = " << i << " , " << j << endl;
cout << "Using compiler-generated int swaper:" << endl;
Swap(i, j); //matches original template
cout << "Now,i,j = " << i << "," << j << endl;
job sue = { "Susan Yaffee",73000.60,7 };
job sidney = { "Sidney Taffee",78060.72,9 };
cout << "Before job swapping:" << endl;
Show(sue);
Show(sidney);
Swap(sue, sidney);
cout << "After job swapping:" << endl;
Show(sue);
Show(sidney);
system("pause");
return 0;
}
//函数定义
template
void Swap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template
void Swap(T a[], T b[],int n) //general version
{
T temp;
for (int i = 0; i < n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
template <> void Swap(job& j1, job& j2) //specialization
{
double t1;
int t2;
t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;
t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}
void Show(job& j)
{
cout << j.name << ": $" << j.salary
<< "on floor" << j.floor << endl;
}
1.显式实例化。其语法是,声明所需要的种类——用<>符号指示类型,并在声明前加上关键字template: template void Swap
2.显式具体化:使用下面两个等价声明之一:
显式具体化在关键字template后包含<>,而显式实例化没有。
警告:⚠试图在同一个文件中使用同一种类型的显式实例和显式具体化将出错。
隐式实例化、显式实例化、显式具体化统称为具体化。他们的相同之处在于他们表示的都是使用具体类型的函数定义,而不是通用描述。
重载解析:
最佳可行函数从最佳到最差的顺序:
头文件中通常包含的内容:
一个实例:
头文件coordin.h
#ifndef COORDIN_H_ //ifndef == id not defined
#define COORDIN_H_
struct polar
{
double distance;
double angle;
};
struct rect
{
double x;
double y;
};
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);
#endif // !COORDIN_H_
file1.cpp
#include
#include "coordin.h"
using namespace std;
int main()
{
rect rplace;
polar pplace;
cout << "Enter the x and y values:";
while (cin >> rplace.x >> rplace.y)
{
pplace = rect_to_polar(rplace);
show_polar(pplace);
cout << "Next two numbers(q to quit):";
}
cout << "Bye!" << endl;
system("pause");
return 0;
}
file2.cpp
#include
#include
#include "coordin.h"
using namespace std;
polar rect_to_polar(rect xypos)
{
polar answer;
answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y);
answer.angle = atan2(xypos.y, xypos.x);
return answer;
}
void show_polar(polar dapos)
{
const double Red_to_deg = 57.2957751;
cout << "distance = " << dapos.distance;
cout << ",angle = " << dapos.angle * Red_to_deg;
cout << " degrees" << endl;
}
C++中使用三种(在C++11中是4种)不同的方案来存储数据
作用域描述了名称在文件(翻译单元)的多大范围类可见。
连接性描述了名称如何在不同单元之间共享。连接性为外部的名称可在文件间共享,连接性为内部的名称只能由一个文件中的函数共享。
自动变量只在包含它们的函数或者代码块中可见。
#include
using namespace std;
void oil(int x);
int main(void)
{
int texas = 11;
int year = 2011;
cout << "In main(),texas = " << texas << ",&texas = ";
cout << &texas << endl;
cout << "In main(),year = " << year << ",&year = ";
cout << &year << endl;
oil(texas);
cout << "In main(),texas = " << texas << ",&texas = ";
cout << &texas << endl;
cout << "In main(),year = " << year << ",&year = ";
cout << &year << endl;
system("pause");
return 0;
}
void oil(int x)
{
int texas = 5;
cout << "In oil(),texas = " << texas << ",&texas = ";
cout << &texas << endl;
cout << "In oil(),x = " << x<< ",&x = ";
cout << &x << endl; //start a block
{
int texas = 113;
cout << "In block,texas = " << texas << ",&texas = ";
cout << &texas << endl;
cout << "In block,x = " << x << ",&x = ";
cout << &x << endl;
} //end a block
cout << "Post-block texas = " << texas;
cout << ",&texas = " << &texas << endl;
}
自动变量存储在栈区。栈是后进先出的,即最后进入栈的变量首先被弹出。函数调用将参数的值放在栈顶,然后重新设置栈顶指针。
1.静态存储持续性变量有3种链接性:外部链接性、内部链接性、无链接性(只能在当前函数或代码块中访问)。
2.编译器将分配所固定的内存块来存储所有的静态变量。静态变量如果没有初始化,编译器将把它设置为0。在默认情况下,静态数组和结构将每个元素或成员的所有位都设置为0。这种变量被称为零初始化的。
3.创建三种静态持续变量:
............
int global = 1000; //static duration,external linkage
static int one_file = 50; //static duration,internal linkage
int main()
{
......
}
void funct1(int n)
{
static int count = 0; //static duration,no linkage
int llama = 0;
}
void funct2(int n)
{
......
}
所有静态变量在整个程序执行期间都存在。只不过count只能在funct1()中使用它。global和one_file的作用域为整个文件,即在从声明位置到文件结束的范围内都可以被使用。具体说,可以在main()、funct1()、funct2()中使用它们。由于one_file的链接性为内部,因此只能在包含上述代码的文件中使用它们。由于global的链接性为外部,因此可以在程序的其他文件中使用它。
4.静态初始化:在编译器处理文件(翻译单元)初始化变量。
动态初始化:变量在编译后初始化。
零初始化和常量表达式初始化被统称为静态初始化。首先,所有静态变量都被零初始化,而不管程序员是否显示地初始化了它。接下来,如果使用常量表达式初始化了变量,编译器再执行常量表达式初始化。
链接性为外部的变量通常称为外部变量。他们的存储持续性为静态,作用域为整个文件。外部变量也称全局变量。
单定义规则:
C++的两种变量声明:
如果要在多个文件中使用外部变量,只需要在一个文件中包含该变量的定义,但在使用该变量的其他所有文件中,都必须使用关键字extern声明它。
external.cpp
#include
using namespace std;
//external variable
double warming = 0.3; //warming defined
void update(double dt);
void local();
int main(void)
{
cout << "Global warming is" << warming << "degrees." << endl;
update(0.1);
cout << "Global warming is" << warming << "degrees." << endl;
local();
cout << "Global warming is" << warming << "degrees." << endl;
system("pause");
return 0;
}
support.cpp
#include
using namespace std;
extern double warming;
void update(double dt);
void local();
void update(double dt)
{
extern double warming;
warming += dt;
cout << "Updateing global warming is" << warming << "degrees." << endl;
}
void local()
{
double warming = 0.8;
cout << "Local warming = " << warming << "degrees." << endl;
cout << "But global warming = " << ::warming << endl; //加了;;之后0.4,不加就是0.8
}
local()函数表明,定义与全局变量同名的局部变量后,局部变量将隐藏全局变量。
::作用域解析运算符。放在变量名前面时,该运算符表示使用变量的全局版本。
twofile1.cpp
#include
using namespace std;
int tom = 3;
int dick = 30;
static int harry = 300;
void remote_access();
int main()
{
cout << "main() reports the following address:" << endl;
cout << &tom << " = &tom," << &dick << " = &dick, ";
cout << &harry << "= &harry" << endl;
remote_access();
system("pause");
return 0;
}
twofile2.cpp
#include
using namespace std;
extern int tom;
static int dick = 0;
int harry = 200;
void remote_access()
{
cout << "remote_access() reports the following address:" << endl;
cout << &tom << " = &tom," << &dick << " = &dick, ";
cout << &harry << "= &harry" << endl;
}
总结:
1.如果文件中定义了一个静态外部变量,其名称与另一个文件声明的常规外部变量相同,则在该文件中,静态变量将隐藏常规外部变量。
2.可使用外部变量在多文件程序的不同部分之间共享数据。可使用链接性为内部的静态变量在同一个文件中的多个函数之间共享数据。(名称空间提供了另一种共享数据的方法)
1.在两次函数调用之间,静态局部变量的值将会保持不变。另外,如果初始化了静态局部变量,则程序只在启动时进行一次初始化。以后再调用函数时,将不会像自动变量那样再次被初始化。
2.程序使用循环丢弃余下的字符。
cin.get(input, ArSize); //当输入的字符数大于目标数组的话,cin.get(input, ArSize)将一直读取输入。
//直到到达行尾或者读取了ArSize-1个字符为止。他把换行符留在了输入队列中。
//该程序使用cin.get(next)读取行输入后的字符。如果next是换行符,说明cin.get()读取了整行
//否则说明行中还有字符没有被读取。随后,程序使用一个循环来丢弃余下的字符
cin.get(next);
while (next != '\n')
{
cin.get(next);
}
1.cv-限定符
2.说明符(在同一个声明中不能使用多个说明符,但thread_local除外,它可以与static或extern结合使用)
所有函数的存储持续性都自动为静态的,即在整个程序执行期间都存在。
在默认情况下,函数的链接性为外部的,即可以在文件间共享。
1.编译器使用3块独立的内存:一块用于静态变量,一块用于自动变量,另一块用于动态存储。
2.定位new运算符:
#include
struct chaff
{
char dorss[20];
int slag;
};
char buffer1[50];
char buffer2[500];
int main(void)
{
chaff* p1, * p2;
int* p3, * p4;
//常规new运算符
p1 = new chaff;
p3 = new int[20];
//定位new运算符
p2 = new(buffer1) chaff;
p4 = new(buffer2) int[20];
}
定位运算符:从buffer1中分配空间给结构chaff,从buffer2中分配空间给一个包含20个元素的int数组。
#include
#include //定位new运算符头文件
using namespace std;
const int BUF = 512;
const int N = 5;
char buffer[BUF];
int main(void)
{
double* pd1, * pd2;
int i;
cout << "Calling new and placement new:" << endl;
pd1 = new double[N];
pd2 = new(buffer)double[N];
for (int i = 0; i < N; i++)
pd2[i] = pd1[i] = 1000 + 20.0 * i;
cout << "Mmemory address:" << endl;
cout << "heap:" << pd1 << " static:" << (void*)buffer << endl;
cout << "Memory contents:" << endl;
for (i = 0; i < N; i++)
{
cout << pd1[i] << " at " << &pd1[i] << ";";
cout << pd2[i] << " at " << &pd2[i] << endl;
}
cout << endl;
double* pd3, * pd4;
pd3 = new double[N];
pd4 = new(buffer)double[N]; //overwrite old data
for (i = 0; i < N; i++)
pd3[i] = pd4[i] = 1000 + 40.0 * i;
cout << "Memory contents:" << endl;
for (i = 0; i < N; i++)
{
cout << pd3[i] << " at " << &pd3[i] << ";";
cout << pd4[i] << " at " << &pd4[i] << endl;
}
cout << endl;
cout << "Calling new and placement new a third time:" << endl;
delete[] pd1;
pd1 = new double[N];
pd2 = new(buffer + N * sizeof(double)) double[N];
for (i = 0; i < N; i++)
pd2[i] = pd1[i] = 1000 + 60.0 * i;
cout << "Memory contents:" << endl;
for (i = 0; i < N; i++)
{
cout << pd1[i] << " at " << &pd1[i] << ";";
cout << pd2[i] << " at " << &pd2[i] << endl;
}
delete[] pd1;
delete[] pd2;
system("pause");
return 0;
}
声明区域:可以在其中进行声明的区域。
潜在作用域:变量的潜在作用域从声明点开始,到其声明区域的结尾。
作用域:变量对程序而言可见的范围。
pail未被限定的名称 Jack::pail限定的名称
1.using声明和using编译指令
using声明由被限定的名称和它前面的关键字using组成,using Jack::pail;
using编译指令:
1.什么是类?
答:类是用户定义的类型的定义。类声明指定了数据如何存储,同时指定了用来访问和操纵这些数据的方法。(类成员函数)
2.类如何实现抽象、封装和数据隐藏?
答:类表示人们可以通过类方法的公有接口对类对象执行的操作,这就是抽象。类的数据成员可以是私有的(默认值),这意味着只能通过成员函数来访问这些数据,这就是数据隐藏。实现的具体细节都是隐藏的,这就是封装。
3.对象和类之间的关系是什么?
类定义了一种类型,包括如何使用它。对象是一个变量或其他数据对象(如由new生成的),并根据类定义被创建和使用。类和对象之间的关系同标准类型与其变量之间的关系相同。
4.除了是函数之外,类函数成员和类数据成员之间的区别是什么?
答:如果创建给定类的多个对象,则每个对象都有其自己的数据内存空间;但所有对象都使用同一组成员函数。
5.类构造函数在何时被调用,类析构函数呢?
答;在创建类对象或显式调用构造函数时,类的构造函数都将被调用。当对象过期时,类的析构函数将被调用。
8.什么是默认构造函数,拥有默认构造函数有何好处?
答:默认构造函数是没有参数或者所有参数都有默认值的构造函数。拥有默认构造函数后,可以声明对象,而不初始化它,即使已经定义了初始化构造函数。他还使得能够声明数组。
10.this和*this是什么?
答:this指针是类方法可以使用的指针,它指向用于调用方法的对象。因此this是对象的地址,*this是对象本身。
1.注意:(p383)不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据。
Time Time::Sum(const Time& t)const
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes / 60;
sum.minutes %= 60;
return sum;
}
节选部分代码参数使用引用的目的是提高效率。传递引用,速度更快,使用的内存将更少。而返回值不是引用,是因为返回的sum是局部变量,在函数结束时将被删除,引用将指向一个不存在的对象。
2.友元函数和成员函数之间的区别是什么?
答:成员函数是类定义一部分,通过特定的对象来调用。成员函数可以隐式得访问调用对象的成员,而无需使用成员运算符。友元函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式地访问类成员,而必须将成员运算符用于作为参数传递的对象。