(接昨天引用)
4、引用型函数返回值
1)可以将函数的返回类型声明为引用,避免函数返回值所带来的内存开销,如果一个函数返回类型被声明为引用,那么该函数的返回值就是一个左值
例:
2)为了避免在函数外部修改引用的目标变量,可以为该引用附加常属性。
1#include<iostream>
2using namespace std;
3struct A{
4 int data;
5 const int& foo(void){
6 return data;
7 }
8 }
9int main(void){
10 Aa={0};//data=0;
11 // a.foo()=100; 将100赋值给data,因为加了const,所以执行不了
12 cout<<a.data<<endl;//100
13 return 0;
14 }
3)回局部变量的引用,危险!不要从函数中返回局部变量的引用,因为所引用的内存会在函数返回以后被释放
1#include<iostream>
2using namespace std;
3struct A{
4 int data;
5 int& foo(void){
6 return data;
7 }
8//返回局部变量的引用,危险!引用这种已经被释放的变量,内存地址可能被其它变量> 所使用,导致结果异常
9 int& bar(void){
10 int data=1000;
11 return data;
12 }
13 int& hum(void){
14 int data=2000;
15 return data;
16 }
17};
18int main(void){
19 Aa={0};//data=0;
20 // a.foo()=100;
21 // cout<<a.data<<endl;//100
22 int& r=a.bar();
23 cout<<r<<endl;
24 a.hum();//不做任何处理
25 cout<<r<<endl;
26 return 0;
27 }
4)可以在函数中返回成员变量,静态变量,全局变量的引用
5引用与指针(笔试题)
1) 引用本质就是指针,但是建议使用引用而不是指针
Double d=1.1234;
Double&rd=d; //rd
Double* constpd=&d ; //*pd
2) 指针可以不初始化,其指向目标可以修改(除指针常量),而引用定义时必须初始化,且一旦初始化引用目标后不能修改。
int a,b;
int *p;
p=&a; //ok
p=*b; //ok
int& r; //error
int* r=a; //r引用变量a
r=a; // 把b的值复制给a
3) 可以定义指针的指针(二级指针),但不能定义引用的指针
int a=10;
int *p=&a;
int**p=&p;//ok
int& r=a;
int&*pr=&r;//error
4) 可以定义指针的引用,但是不能定义引用的引用
int a=10;
int*p=&a;
int*&rp=p;//OK
int& r=a;
int&&rr=r ; //error
5) 可以定义指针数组,但是不能定义引用数组
inta=10,b=20,c=30;
int*arr[3]={&a,&b,&c};//ok 指针数组
int&rarr[3]={a,b,c};//error
6) 可以定义数组引用
intarr[3]={1,2,3};
int(&r)[3]=arr; //r就是数组的别名
arr[0] === r[0];
7) 和函数指针一样,可以定义函数的引用,其语法基本一致
void func(inta.inb){
cout<<a<<b<<endl;
}
Int main(void){
void(* func)(int, int)=func;
pfunc(10,20); // 10 20
void(&rfunc)(int ,int )=func;
rfunc(10,20); //10 20
}
一、 C++类型转换
1、 隐式类型转换
char c=’A’;
int n=c;// 隐式类型转换
void foo(int i){….}
int foo(void){
char c=’A’;
return c;// 隐式类型转换
}
int main(void){
char c=’A’;
foo(c); //u;隐式类型转换
}
2、 显式类型转换
2.1:C语言强制类型转换
int n=(int)c ; // C的强制转换
int n=int (c);// C++ 风格的强制转换
2.2:C语言兼容C的强制转换,同时增加了四中操作符形式的显示转换
1)静态类型转换
目标类型变量=static_cast<目标类型>(源类型变量)
-à主要用于将void *转换为其它类型的指针
-à可以使用所有隐式类型转换的地方
int a;
void *pv=&a;// int * à void* OK
int *pi=pv; //void* à int* ,error, 无法隐式转换
int*pi=static_cast<int *>(pv); OK
例:
1 #include<iostream>
2 using namespace std;
3 int main(void){
4 int* pi=NULL;
5 void* pv=pi;//OK
6 pi=static_cast<int*>(pv);//静态类型转换
7 char c='A';
8 // pi=static_cast<int*>(c); error从char到int*,即从变量值到地址是不合理转换
9 return 0;
10 }
2)动态类型转换
目标类型变量=dynamic_cast<目标类型>(源类型变量)
3)常类型转换
目标类型变量=const_cast<目标类型>(源类型变量)
用于去除一个指针或引用的常属性
int a=100;
constint*pa=&a;
*pa=200 ;//error
int*pa2=const_cast_cast<int *>(pa)
*pa2=200; // OK
例:
1 #include<iostream>
2 using namespace std;
3 int main(void){
4 /*
5 *volatile修饰变量意味着易变的,告诉编译器每次使用该变量时,都从内存中读取,
而不是取寄存器中的副本,防止编译器优化引发错误的结果
9 * */
10 const volatile int ci=100;
11 // int* pci=&ci; error,constint -->int
12 int*pci=const_cast<int*>(&ci);
13 *pci=200;
14 cout<<"ci="<<ci<<endl;
15 cout<<"*pci="<<*pci<<endl;
16 cout<<"&ci"<<(void *)&ci<<endl;//地址的默认类型void *
17 cout<<"pci"<<pci<<endl;
18 }
4)重解释类型转换
目标类型变量=reinterpret_cast<目标类型>(源类型变量)
à任意类型指针或引用之间的转换
à在指针和整型数之间的转换
1 #include<iostream>
2 using namespace std;
3 int main(void){
4 int num=0x12346578;
5 void* pnum=reinterpret_cast<void*>(num);
6 cout<<pnum<<endl;
7 char text[]="0001\000123456\000888888";
8 struct T{
9 char type[5];
10 char acc[7];
11 char passwd[7];
12 };
13 T* pt=reinterpret_cast<T*>(text);
14 cout<<pt->type<<endl;
15 cout<<pt->acc<<endl;
16 cout<<pt->passwd<<endl;
17 return 0;
18 }
0x12346578
0001
123456
888888
小结:C++之父给程序员建议
1、 慎用宏,代之以const \ enum \ inline
#define PAI3.14 è const double PAI =3.14
#defineSTATE_SLEEP 0
#defineSTATE_RUN 1
#defineSTATE_STOP 2
==è enum STATE{ SLEEP,RUN,STOP }
#definemax(a,b) ((a)>(b)?(a):(b)) 预处理阶段
è inline int max(int a,int b){
return a>b?a:b;
}
2、 变量随用随声明同时初始化
3、 尽量用new/delete替换malloc/free
4、 少用void*、指针计算、联合体、强制转换
5、 尽量用string表示字符串,少用C风格char*
二、 类和对象
1、 对象
万物皆对象,任何一种事物都可以看做是对象
2、 面向对象
2.1 如果描述表达对象
通过对象的属性(名词、数量词、形容词)和行为(动词)来表达对象
冰箱:
à属性:品牌、容量、颜色、功耗
à行为:装东西、冷冻
2.2 面向对象的程序设计
对自然世界的观察引入到编程实践中的一种方法
数据抽象:在描述对象时把细节东西剥离出去,只考虑一般性的、有规律性的、统一的东西
3、 类
类是将多个对象的共性提取出来定义的一种新的数据类型,是对对象的属性和行为抽象地描述,对象就是类的实例化。
学生对象:
属性:姓名、年龄、学号
行为:吃饭、睡觉、学习
现实世界 类 虚拟世界
具体的对象--à抽象---à属性/行为-----实例化--à具体的对象
三、 类的定义和实例化
1、 类的一般形式
class / struct 类名:继承方式 基类…..{
访问控制限定符:
类名(形参表):初始化表{…} //构造函数
~ 类名(void){….} // 析构函数
返回类型函数名(形参表)[const ]{ } //成员函数
数据类型 变量名;//成员变量
};
2、 访问控制限定符
public:公有成员,谁都可以访问的成员
private:私有成员,只有类自己内部可以访问的成员
protected:保护成员
注:class 缺省访问控制属性为私有,struct 缺省为公有
class A{
public:
xxxxx ; //公有
private:
xxxxx; // 私有
};
int main{
xxxx
}
例:
#include<iostream>
using namespacestd;
/*struct*/classstudent{
private:
//成员都是似有的
public:
//以下成员都是公有的
//行为
void eat(const string& food){
cout<<"我吃"<<food<<endl;
}
void learn(const string& course){
cout<<"我学"<<course<<endl;
}
void who(void){
cout<<"我叫"<<m_name<<",今年"<<
m_age<<"岁了,学号"<<m_no<<endl;
}
void setName(const string& name){
if(name=="二")
cout<<"你才"<<name<<endl;
else
m_name=name;
}
void setAge(int age){
if(age<0)
cout<<"无效年龄"<<endl;
else
m_age=age;
}
void setNo(int no){
if(no<1000){
cout<<"无效学号"<<endl;
}
else
m_no=no;
}
//封装特性
/*私有成员不能在类的外部直接访问,但是可以提供公有的接口函数间接访问,这样可以对非法数据加以限定,控制合理性*/
private:
// 属性
string m_name;
int m_age;
int m_no;
};
int main(void){
/*原来叫定义一个结构体变量,现在在面向对象变成中叫做创建对象,或对象实例化*/
student s;
s.setName("张飞");
//s.m_name="张飞";error,因为私有成员不能在类的外部直接访问
s.setName("二");
s.setAge(26);
s.setAge(-1);
s.setNo(10001);
s.who();
s.eat("兰州拉面");
s.learn("C++编程");
return 0;
}
你才二
无效年龄
我叫张飞,今年26岁了,学号10001
我吃兰州拉面
我学C++编程
练习:实现一个Dog类,实例化一个Dog对象
Dog属性:犬种、犬龄、体重、毛色
Dog行为:进食、睡觉、玩耍
1 #include<iostream>
2 using namespace std;
3 class dog{
4 private:
5 string d_type;
6 int d_age;
7 int d_weight;
8 string d_color;
9 public:
10 void eat(const string& food){
11 cout<<"我吃"<<food<<endl;
12 }
13 void sleep(const string& slep){
14 cout<<"我爱"<<slep<<endl;
15 }
16 void play(const string& enjoy){
17 cout<<"我爱"<<enjoy<<endl;
18 }
19 void settype(const string& type){
20 d_type=type;
21 }
22 void setage(const int& age){
23 d_age=age;
24 }
25 void setweight(const int& weight){
26 d_weight=weight;
27 }
28 void setcolor(const string& color){
29 d_color=color;
30 }
31 void who(void){
32 cout<<"我是"<<d_type<<"品种,"<<"犬龄"<<d_age<<endl;
33 cout<<"体重"<<d_weight<<",毛色是"<<d_color<<endl;
34 }
35 };
36 int main(void){
37 dog wang;
38 wang.settype("goldenFair");
39 wang.setage(5);
40 wang.setweight(30);
41 wang.setcolor("golden");
42 wang.who();
43 wang.eat("meat");
44 wang.sleep("sleep");
45 wang.play("play");
46 return 0;
47 }
我是goldenFair,犬龄5
体重30,毛色是golden
我爱吃meat
我爱sleep
我爱play
3、 构造函数
class 类名{
类名(){
构造函数体;
}
};
1) 函数名与类名相同,而且没有返回类型
2) 构造函数在对象被创建时自动被调用
3) 构造函数主要负责初始化成员变量,以及分配必要的资源
4) 构造函数在每个对象的生命周期一定会被调用,而且只会被调用一次
例:
#include<iostream>
using namespacestd;
/*struct*/classstudent{
private:
//成员都是似有的
public:
//以下成员都是公有的
student(const string& name,int age,intno){
cout<<"构造函数"<<endl;
m_name=name;
m_age=age;
m_no=no;
}
//行为
void eat(const string& food){
cout<<"我吃"<<food<<endl;
}
void learn(const string& course){
cout<<"我学"<<course<<endl;
}
void who(void){
cout<<"我叫"<<m_name<<",今年"<<
m_age<<"岁了,学号"<<m_no<<endl;
}
void setName(const string& name){
if(name=="二")
cout<<"你才"<<name<<endl;
else
m_name=name;
}
void setAge(int age){
if(age<0)
cout<<"无效年龄"<<endl;
else
m_age=age;
}
void setNo(int no){
if(no<1000){
cout<<"无效学号"<<endl;
}
else
m_no=no;
}
//封装特性
/*私有成员不能在类的外部直接访问,但是可以提供公有的接口函数间接访问,这样可以对非法数据加以限定,控制合理性*/
private:
// 属性
string m_name;
int m_age;
int m_no;
};
int main(void){
/*原来叫定义一个结构体变量,现在在面向对象变成中叫做创建对象,或对象实例化*/
#if 0
student s;
s.setName("张飞");
//s.m_name="张飞";error,因为私有成员不能在类的外部直接访问
s.setName("二");
s.setAge(26);
s.setAge(-1);
s.setNo(10001);
s.who();
s.eat("兰州拉面");
s.learn("C++编程");
#endif
//以有参方法创建一个对象,参数传递给构造函数
student s("张飞",25,10011);//构造对象
//和上面写法完全等价
//student s=student("张飞",25,10011);
s.who();
s.eat("兰州拉面");
s.learn("C++编程");
return 0;
}
张飞
4、 类的声明和定义可以放在不同的文件中
类的声明放在stu.h中
对于大型程序开发:
vi stu.h
1 #ifndef __STU_H_
2 #define __STU_H_
3 #include<iostream>
4 using namespace std;
5 class student{
6 public:
7 student(const string& name);
8 void who(void);
9 private:
10 string m_name;
11 };
12 #endif//__STU_H_
类的实现放在stu.cpp中
vi stu.cpp
1 #include"4stu.h"
2 //通过作用域限定说明属于哪个类
3 student::student(const string& name){
4 m_name=name;
5 }
6 void student::who(void){
7 cout<<m_name<<endl;
8 }
使用该类的代码通常还会在其它文件中main.cpp
vi main.cpp
1 #include"4stu.h"
2 int main(void){
3 student s("张飞"); //在栈区创建一个对象
4 s.who();
5 return 0;
6 }
tarena@tarena-virtual-machine:~/day38$g++ 4main.cpp 4stu.cpp
tarena@tarena-virtual-machine:~/day38$./a.out
5、 对象的创建和销毁
1) 在栈区创建单个对象
类名:对象(构造实参表)
类名:对象=类名(构造实参表);//和上面等价
2) 在栈区创建对象数组
类名对象数组[元素个数]={类名(构造实参表)…..}
3) 在堆区创建单个对象
创建:类名* 对象指针= new 类名(构造实参表);
销毁:delete 对象指针;
4) 在堆区创建/销毁对象数组
创建:类名* 对象指针=new 类名[元素个数]{类名(构造实参表),….}
注:这种初始化的语法仅在C++11中支持
销毁:delete[ ]对象指针
例:
#include"4stu.h"
int main(void){
//在栈区创造单个对象
student s("张飞");
//等价于student s=student("张飞")
s.who();
//在栈区创建多个对象
student sa[3]={student("悟空"),
student("关羽"),
student("赵云")};
sa[0].who();
sa[1].who();
sa[2].who();
//在堆区创建单个对象
student* ps=new student("曹操");
ps->who();
delete ps;
ps=NULL;
//在堆区创建对象数组,初始化表C++11支持
ps=new student[3]{
student("宝玉"),student("宝强"),student("黛玉")};
ps[0].who();
ps[1].who();
ps[2].who();
delete[]ps;
ps=NULL;
return 0;
}
tarena@tarena-virtual-machine:~/day38$g++ 4main.cpp 4stu.cpp
4main.cpp: 在函数‘int main()’中:
4main.cpp:20:21:警告: 扩展初始值设定列表只在 -std=c++0x 或 -std=gnu++0x 下可用 [默认启用]
tarena@tarena-virtual-machine:~/day38$./a.out
张飞
悟空
关羽
赵云
曹操
宝玉
宝强
黛玉
练习:实现一个电子时钟类,要求用其构造函数参数接收当前的系统时间,以秒为单位运行。
class clock{
public:
clock(time_tt){
tm*local=localtime(&t);
时=local->tm_hour
分=local->tm_min
秒=local->sec
};
Void run(void){
While(1){
计时+1s;
Sleep(1);
}
}
private
时、分、秒
};
int main{
clock(time(NULL));
}
//获取日历时间,1970,1,1,00:00:00—现在经历秒数
time_t time(NULL);
//将日历时间转换为本地时间,将结果保存tm结构体
Struct tm* local=localtime(&t)
#include<iostream>
#include<cstdio>
using namespace std;
class c_Clock{
public:
c_Clock(time_t t){
tm* local=localtime(&t);
m_hour=local->tm_hour;
m_min=local->tm_min;
m_sec=local->tm_sec;
}
void run(void){
while(1){
show();
tick();
}
}
private:
void show(void){
printf("\r%02d:%02d:%02d",m_hour,m_min,m_sec);
fflush(stdout);//目的是清空缓冲,强制结果马上显示在屏幕上
//调用printf时,输出结果一般会被标准库缓存起来,不一定能及时写在屏幕上,但是fflush(stdout)能强制把缓存内容输出
}
void tick(void){
sleep(1);
if(++m_sec==60){
m_sec=0;
if(++m_min==60){
m_min=0;
if(++m_hour==24){
m_hour=0;
}
}
}
}
private:
int m_hour;
int m_min;
int m_sec;
};
int main(void){
c_Clock clock(time(NULL));
clock.run();
return 0;
}