第2章 变量和基本类型
2.1占用字节
cout<<sizeof(double)<<" "<<sizeof(float)<<endl;
2.5 引用
1、引用
(1)引用和指针一样,是绑定对对象的另外一个名字,改变它或者它绑定的对象,都会使双方改变
(2)必须是一个对象的别名, int &b=1;是错的
(3)若绑定的对象时const类型,那么引用也必须是const类型 如: const int a=1; (必须初始化),const int &b=a;
引用和复制构造函数的关系:
#include "stdafx.h"
#include<iostream>
#include<string>
using namespace std;
#include <iostream>
using namespace std;
class A
{
public:
A():men(0){cout<<"A"<<endl;};
A(const A & a){men=a.men; cout<<"copy A"<<endl;}
int men;
~A(){cout<<"~A"<<endl;}
}a;
const A &p(A &a)
{
cout<<a.men<<endl;
a.men=1;
return a;
}
int main(int argc, char *argv[])
{
A a1=p(a);
a.men=1;
cout<<a1.men<<a.men<<endl;
return 0;
}
输出:
A
0
copy A
11
~A
~A
也就是说在用引用传值的时候,只是起的一个别名,共享空间,再加上const 则无法修改原来空间的值,只能读
而当返回值为引用的时候,那么会调用复制构造函数。根据构造函数来初始化引用返回的对象。
另外当形参不是引用的时候,如A a也会调用复制构造函数
注:千万不要引用局部变量,如在函数P中写 A b; return b;的话,那么 当函数执行完毕 会有 A ~A 也就是析构完成了,然后才执行复制构造函数。
哈哈终于明白了。
引用和一般返回值的问题:
如函数:
A &p(A &a)
{
cout<<a.men<<endl;
a.men=1;
return a;
}
只能返回a,这样调用复制构造函数,形成一个新的对象,与原来的对象完全独立。修改成员相互不影响,但是如返回局部变量如:
A &p(A &a)
{
cout<<a.men<<endl;
A b;
return b;
}
则,函数结束后 b析构 ,A执行了构造函数,但是其内容却没有了。
而对于一般返回如:
A p(A &a)
{
cout<<a.men<<endl;
A b;
return b;
}
则是独立于局部变量的。
结论:如果返回局部变量,就用一般返回。如果用的是全局的,那么引用和一般返回都可以。而指针不调用构造函数,只是返回指向原空间的指针。推荐返回用一般返回。
追加:引用就是绑定,一般是不会产生构造函数的,但如果是函数返回引用的话,还是会产生构造函数的,但引用返回和一般返回有什么区别呢?因此对于A &p(A &a),返回的对象是对函数返回值的绑定,因此,当返回值没有了,那么对象也没有了。反之A p(A &a),返回对象为对函数返回值的复制,当返回值没有了,对象还在。(适用于局部变量返回的解释,但是若为形参返回,则解释不通)
2、char的几种定义形式和几种形参形式:
几种定义形式:
char s[10]={'a','b'}//字符数组
char s[]="abc"//不定长数组
char *s="abc"//指向常量字符串的指针
<strong>//在前两种情况中都可以用s[1]='a';来修改数组的值。
//但是在第三种情况中不能修改指针指向常量//的值.如:s[1]是错误的
//但是可以修改指针的指向如:s="cde";</strong>
几种形参形式
char *s //如果传进来的是数组的话 是可以用s[1]来修改的
char s[10]
const char *s
char (&s([10]
const char (&s)[10]
这几种情况就不要分析了
3、枚举
enum A{a,b,c}; 那么a,b,c的值为012 后面每一个变量比前面的要加上1
4、预编译
在原来的头文件中使用这样的语句来防止多次编译的
#ifndef FILE_H
#define FILE_H
#endif
但是也可以用
#pragma once
第3章标准库类型
两种最重要的标准库类型是string 和vector
3.2 标准库string 类型
1、string对象的定义和初始化
int _tmain(int argc, _TCHAR* argv[])
{
string s1,s2;
cin>>s1>>s2;
cout<<s1<<s2<<endl;
return 0;
}
输入:45 89
输出:4589
也就是说cin遇到‘ ’就终止第一个变量的读取,而从下一个非' '字符重新获取,以Enter结束读值
(2)getline读取整行文本
string ;
getline(cin,s);
cout<<s<<endl;
输入: 45 78
输出: 45 78
也就是说变量能够接收 空格
(3)string对象的一些操作
s.empty()
s.size()
s[n]
s1+s2//连接他们,组成新串
s1=s2//将s1的内容替换为s2的副本
v1==v2 //判断是否相同
(4)string::size_type类型
string s1="aaa";
string::size_type a=s1.size();
cout<<a<<endl;
输出:3
这里string::size_type可以简写为size_t;
注:这里size_t不要写成int,但是size_t是机器的最大位数,而int可能较小,会溢出
(5)s[n]
string s1="abc";
string::size_type a=s1.size();
cout<<s1[2]<<endl;
输出:c
注:下标从0开始
(6)string对象中的字符处理
我们有时候需要对string对象中的单个字符进行处理
一些常用的函数如下:
isalnum 字母或数字
isalpha 字母
iscntrl
isdigit
isgraph
islower
isprint
ispuct //是标点符号
isspace
isupper
isxdigit //16进制数
tolower //转化为小写
toupper //转化为大写
3.3 标准库vector 类型
1、vector对象的定义和初始化
vector <int> v(v1) //复制元素v1的元素进v
vector <int> v(10,-1) //10个-1
2、vector对象的操作
v.empty()
v.size()
v.push_back()
v[n]
v1=v2
3.4 迭代器简介
1、容器的iterator类型
(1)每种容器都定义了自己的迭代器类型,如vector
vector<int>::iterator iter;
(2)begin和end操作
iter=v.begin()//指向第一个元素v[0]
而end是一个不存在的元素,是末端元素的下一个,只是用来判定是否处理完容器内的所有元素,若容器为空,那么begin=end;
(3)迭代器应用示例
vector<int> v(5);
for(vector<int>::iterator iter=v.begin();iter!=v.end();iter++)
cout<<*iter<<endl;
输出为:
0
0
0
0
0
注:这里的迭代器要声明类型为 vector<int>::
同样,若是size_type,可以这样写
vector<int> v(5);
for(vector<int>::size_type iter=0;iter<v.size();iter++)
cout<<v[iter]<<endl;
注:这里的vector<int>::size_type 可以写为size_t
(5)const_iterator
只允许读,不允许修改,也就是说*iter=0这样是不允许的
3.5标准库biset类型
正如名字一样,位设置,都是二进制位,0 1
1、初始化
string s("01111010");
bitset<10> b(s,s.size() - 2);//倒数两个字符,为01,其余的用0填充,b为0100000000
bitset<10> b(s,4,4);//b为0101 其余用0填充 填充时候,是从后面往前面走的。
2、biset对象上的操作
b.any() //是否存在1
b.none()
b.count()
b.size()
b[pos]
b.test(pos)
b.set()
b.set(pos)
b.reset()
b.flip()
b.flip(pos)
b.to_ulong()
第4章 数组和指针
应该尽量使用vector类型,只有在强调速度时,才在类的内部使用数组和指针。
1、string使用指针所遇到的问题
string s("01111010");
string *p=&s;
//p=p+1;//error 同样无法用p[1]
cout<<*p<<endl;
也就是说指向string的指针是一个整体,就跟类是一样的。
2、使用指针可以访问数组
int a[]={1,3,5};
int *p=&a[2];
cout<<*p<<endl;
输出:
5
同样可以访问字符串数组char,就跟前面讲到的一样
const 修饰指针,则无法改变所指向对象的值。
但是可以更改指向
3、指针和const限定符
(1)指向const对象的指针
const int *p=0;
*p=1;//error不允许修改指针指向对象的值
p=&a;
(2)const指针
表示指针是const类型,不允许修改指针的指向,但是可以修改指针指向对象的值。
int *const p=0;
*p=1;
p=&a;//error
注意这种定义类型,必须是 int *const p 说明是int的指针
(3)指向const对象的const指针
const int *const p=0;
*p=1;//error
p=&a;//error
(4)指针和typedef
string s;
typedef string *pstring;
pstring const p1=&s;
const pstring p2=&s;
string *const p3=&s;
这几个指针都是const指针。
产生const难以理解的原因是因为const既可以放在类型前面又可以放在类型后面(记住是类型)也就是说
int const 和const int是一样的,但是
int *const是无法交换的 因为那是*const,好的 明白了。
4.3 C风格字符串
记住不要忘记字符串结束符null,尽可能的使用string,而要少使用char*
#include <cstring>
1、标准库函数
strlen(s)
strcmp(s1,s2)
strcat(s1,s2)
strcpy(s1,s2)
strncat(s1,s2,n)将s2的前n个字符连接到s1的后面
strncpy(s1,s2,n)
2、永远不要忘记字符串结束符
(1)
char a[]={'a','b','\0'};
cout<<strlen(a)<<endl;
输出:2
(2)
char a[]={'a','b'};
cout<<strlen(a)<<endl;
输出:15
出现了问题
(3)
但是
char *p="afsfds";
cout<<strlen(p)<<endl;
这样子做是没有问题的,输出6
不过这种指针相当于是const 对象的指针
3、创建动态数组
const int size=5;
int ar[size];//ok
int size_a=1;
int ar[size_a];//error 不是动态分配
int *p=new int[size_a];
delete [] p;//加上[]才释放掉整个数组的空间
有时候需要动态确定数组的长度,而不仅仅是用const int 来定义数组的长,这个时候就需要用到动态分配数组
在c中用malloc()和free()
在c++中,用new 和delete
4、新旧代码的兼容
string s("aaa");
char *p=s.c_str();
意思为转换回c风格字符串的表示方法
使用数组来初始化vector对象
5、多维数组
int a[2][4]={{1,2,3,4},{7,8,9,10}};
int (*p)[4]=&a[1]; //p为指向具有4个int数组的 指针
cout<<*p[0]<<endl;
p=&a[0];
cout<<*p[0]<<endl;
输出:
7
1
注意必须为int (*p)[4] 必须有()
第5章 表达式
第6章 函数
1、局部静态对象
#include "stdafx.h"
#include <iostream>
#include <set>
using namespace std;
size_t count_calls()
{
static size_t ctr=0;
return ++ctr;
}
int main()
{
for(int i=0;i<3;i++)
{
cout<<count_calls()<<endl;
}
return 0;
}
输出:
1
2
3
请按任意键继续. . .
思想:
(1)这里静态局部变量为什么可以多次定义,因为函数多次执行之后,该变量是一直存在的?
2、为什么程序执行的时候是编译、链接、生成
因为是分不同的源文件,不同编译的,这样当一个源文件发生改变的时候,只需要重新编译那个源文件,再链接即可。
3、使用引用的一个重要原因是,当一些对象很大的时候,拷贝效率是很低的。
4、const的一些问题
(1)不能把const 字面值 以及需要类型转换的(如无符号传给有符号的)传递给普通引用的形参
(2)但是可以把普通形参传递给const引用形参
5、数组形参
(1)当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针
(2)以下几种情况是一样的
void print(int *a)
void print(int a[])
void print(int a[10])
6、数组引用形参
void print(int (&a)[10])
int *matrix[10] //10个指针
int (*matrix)[10]
int matrix[][10]//下面两种是一样的
这里需要特别注意,有机会要用一下,因为以前都是声明指针的指针如int **a;
7、main函数的一些知识
(1)int main(int argc,char **argv)
argv的第一个元素指向程序的名字或者一个空字符串
argc表示字符串的个数
(2)若没有返回语句,则会默认加上一句return
8、含有可变形参的函数
initializer_list相当于一个vector但是只能存放常量,遍历方式和vector也是一样的。
9、函数返回引用
这种用法一般为,形参也为绑定,然后返回绑定的形参。这样可以由返回的值来修改绑定值。
10、返回值为vector类型,这样可以利用返回值进行初始化如 return {"fdasfd","get"}
11、