C++Primer 笔记之----变量和函数

第2章 变量和基本类型

2.1占用字节

32位机器上:
cout<<sizeof(long)<<" "<<sizeof(long int)<<" "<<sizeof(long long)<<endl;
cout<<sizeof(double)<<" "<<sizeof(float)<<endl;
输出:
4 4 8
8 4
也就是说只有long long 以及double是占用8个字节的。

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、

 


 


 



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值