十一 类型转换
1 隐式类型转换
char c = 'A';
int i = c;//隐式
-----------------
void func(int i){}
func(c);//隐式
-----------------
int func(void){
char c='A';
return c;//隐式
}
2 显示类型转换
2.1 C++兼容C中强制类型转换
char c = ‘A’;
int i = (int)c;//C风格
int i = int©;//C++风格
2.2 C++扩展了四种操作符形式显式转换
1)静态类型转换:static_cast
语法:
目标变量 = static_cast<目标类型>(源类型变量);
适用场景:
主要用于将void*转化为其它类型的指针,转换时做静态检查,编译时进行
double d=3.14
int i=static_cast<int>(d)
int *p=static_cast<int*>(malloc(sizeof(int)*10))
2)动态类型转换:dynamic_cast//后面讲
语法:
目标变量 = dynamic_cast<目标类型>(源类型变量);
3)去常类型转换:const_cast
语法:
目标变量 = const_cast<目标类型>(源类型变量);
适用场景:
主要用于去掉指针或引用const属性.
const volatile int CI=12345;
int *pci=const_cast<int *>(&CI)
4)重解释类型转换:reinterpret_cast
语法:
目标变量=reinterpret_cast<目标类型>(源类型变量);
适用场景:
在指针和整型数进行显式转换.
任意类型指针或引用之间显式转换.
eg:已知物理内存地址0x12345678,向该地址存放一个整型数100?
int* paddr = reinterpret_cast<int*>(0x12345678);
*paddr = 100;
小结:
1 慎用宏,可以使用const、enum、inline替换
#define PAI 3.14
–》const double PAI = 3.14;
#define SLEEP 0
#define RUN 1
#define STOP 2
–》enum STATE{SLEEP,RUN,STOP};
#define Max(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*/char[],string和vector可以简化程序
十二 类和对象//了解
1 什么是对象
万物皆对象,任何一种事物都可以看做是对象.
2 如何描述对象
通过对象的属性和行为来描述对象.
3 面向对象程序设计
对自然世界中对象观察和描述引入到编程中一种理念和方法,这种方法称为"数据抽象",即在描述对象时把细节东西玻璃出去,只考虑一般性的、有规律性的、统一性的东西.
4 什么是类
类就是将多个对象共性提取出来定义的一种新的数据类型,是对 对象 属性和行为的抽象描述.
现实世界 类 虚拟世界
具体对象–抽象–>属性/行为–实例化–>具体对象
十三 类的定义和实例化
1 类定义的一般语法形式
struct/class 类名:继承方式 基类,…{
访问控制限定符:
类名(形参表):初始化列表{}//构造函数
~类名(void){}//析构函数
返回类型 函数名(形参表){}//成员函数
数据类型 变量名;//成员变量
};
2 访问控制限定符
1)public:公有成员,任何位置都可以访问。
2)private:私有成员,只有类自己的成员函数才能访问,以及类的友元访问
3)protected:保护成员(后面讲)
注:如果struct定义类默认的访问控制属性是public;而如果是class定义类默认的访问控制属性是private.
eg:
struct/class XX{
int m_a;//默认访问属性
public:
int m_b;//公有成员
private:
int m_c;//私有成员
public:
int m_d;//公有成员
};
#include <iostream>
using namespace std;
//原来:定义结构体
//现在:定义类
//struct Student{
class Student{
public:
//行为:成员函数
void eat(const string& food){
cout << "我在吃" << food << endl;
}
void sleep(int hour){
cout << "我睡了" << hour << "小时"
<< endl;
}
void learn(const string& course) {
cout << "我在学" << course << endl;
}
void who(void){
cout << "我叫" << m_name << ",今年" <<
. m_age << "岁,学号是" <<m_no<<endl;
}
public:
/* 类中的私有成员不能在外部直接访问,但是可
* 以提供类似如下的公有成员函数来间接访问,
* 在函数中可以对非法数据加以限定控制业务
* 逻辑的合理性,这种编程思想就是"封装".*/
void setName(const string& newName){
if(newName == "二")
cout << "你才二" << endl;
else
m_name = newName;
}
void setAge(int newAge){
if(newAge < 0)
cout << "无效年龄" << endl;
else
m_age = newAge;
}
void setNo(int newNo){
if(newNo < 0)
cout << "无效学号" << endl;
else
m_no = newNo;
}
private:
//属性:成员变量
string m_name;
int m_age;
int m_no;
};
int main(void){
//原理:定义结构体变量
//现在:创建对象/实例化对象/构造对象
Student s;
/*s.m_name = "张飞";
s.m_name = "二";
s.m_age = 25;
s.m_no = 10011;*/
s.setName("张翼德");
s.setName("二");
s.setAge(26);
s.setAge(-2);
s.setNo(10086);
s.setNo(-1);
s.who();
s.eat("牛肉拉面");
s.sleep(8);
s.learn("C++编程");
return 0;
}
3 构造函数(constructor)
1)语法
class 类名{
类名(参数表){
主要负责初始化对象,即初始化成员变量。
}
};
2)函数名和类名一致,没有返回类型。
3)构造函数在创建对象时自动被调用,不能像普通的成员函数一样显式的调用.
4)在每个对象的生命周期,构造函数一定会被调用,且仅会被调用一次。
练习:实现一个电子时钟类,使用构造函数初始化时钟的时间为当前的系统时间,并可以以秒为单位运行
1. #include <iostream>
2.#include <cstdio>
3.#include <ctime>
4.#include <unistd.h>
5.using namespace std;
6.class Clock{
7.public:
8. Clock(time_t t){
9. tm* local = localtime(&t);
10. m_hour = local->tm_hour;
11. m_min = local->tm_min;
12. m_sec = local->tm_sec;
13. }
14. void run(void){
15. while(1){
16. printf("\r%02d:%02d:%02d",
17. m_hour,m_min,m_sec);
18. fflush(stdout);//刷新标准输出流
19. if(60 == ++m_sec){
20. m_sec = 0;
21. if(60 == ++m_min){
22. m_min = 0;
23. if(24 == ++m_hour){
24. m_hour = 0;
25. }
26. }
27. }
28. sleep(1);
29. }
30. }
31.private:
32. int m_hour;
33. int m_min;
34. int m_sec;
35.};
36.int main(void){
37. Clock c(time(NULL));
38. c.run();
39. return 0;
40.}
提示:
class Clock{
public:
构造函数(time_t t){
tm* local = localtime(&t);
时 = local->tm_hour;
分 = local->tm_min;
秒 = local->tm_sec;
}
void run(void){
while(1){打印当前时间;计时+1秒;sleep(1);}
}
private:
int 时,分,秒;
};
Clock c( time(NULL) );
c.run();
很像字符串的使用方法
#include <iostream>
using namespace std;
//原来:定义结构体
//现在:定义类
//struct Student{
class Student{
public:
//构造函数
Student(const string&name,int age,int no){
cout << "构造函数" << endl;
m_name = name;
m_age = age;
. m_no = no;
}
//行为:成员函数
void eat(const string& food){
cout << "我在吃" << food << endl;
}
void sleep(int hour){
cout << "我睡了" << hour << "小时"
<< endl;
}
void learn(const string& course) {
cout << "我在学" << course << endl;
}
void who(void){
cout << "我叫" << m_name << ",今年" <<
m_age << "岁,学号是" <<m_no<<endl;
}
public:
/* 类中的私有成员不能在外部直接访问,但是可
* 以提供类似如下的公有成员函数来间接访问,
* 在函数中可以对非法数据加以限定控制业务
* 逻辑的合理性,这种编程思想就是"封装".*/
void setName(const string& newName){
if(newName == "二")
cout << "你才二" << endl;
else
m_name = newName;
}
void setAge(int newAge){
if(newAge < 0)
cout << "无效年龄" << endl;
else
m_age = newAge;
46. }
47. void setNo(int newNo){
48. if(newNo < 0)
49. cout << "无效学号" << endl;
50. else
51. m_no = newNo;
52. }
53.private:
54. //属性:成员变量
55. string m_name;
56. int m_age;
57. int m_no;
58.};
59.int main(void){
60. //创建对象(自动调用构造函数)
61. //(...):指定构造函数需要的实参
62. Student s("张飞",25,10011);
63. s.who();
64.
65. //构造函数不能显式调用
66. //s.Student("张三",26,10086);
67.
68. return 0;
69.}
4 对象的创建和销毁
1)在栈区创建单个对象 //重点掌握
类名 对象(构造实参表);//直接初始化
类名 对象=类名(构造实参表);//拷贝初始化(实际等价)
eg:
string s;
string s(“hello”);
string s = string(“hello”);//string s = “hello”;
2)在栈区创建多个对象(对象数组)
类名 对象数组[元素个数] = {
类名(构造实参表),类名(构造实参表),…};
3)在堆区创建/销毁单个对象 //重点掌握
创建:
类名* 对象指针 = new 类名(构造实参表);
注:new操作符会先分配内存再自动调用构造函数,完成对象的创建和初始化;而如果是malloc函数只能分配内存,不会调用构造函数,不具备创建对象能力.
销毁:
delete 对象指针;
4)在堆区创建/销毁多个对象
创建:
类名* 对象指针 = new 类名[元素个数] {
类名(构造实参表),类名(构造实参表),…};
销毁:
delete[] 对象指针;
1.#include <iostream>
2.using namespace std;
3.//原来:定义结构体
4.//现在:定义类
5.//struct Student{
6.class Student{
7.public:
8. //构造函数
9. Student(const string&name,int age,int no){
10. cout << "构造函数" << endl;
11. m_name = name;
12. m_age = age;
13. m_no = no;
14. }
15. //行为:成员函数
16. void eat(const string& food){
17. cout << "我在吃" << food << endl;
18. }
19. void sleep(int hour){
20. cout << "我睡了" << hour << "小时"
21. << endl;
22. }
23. void learn(const string& course) {
24. cout << "我在学" << course << endl;
25. }
26. void who(void){
27. cout << "我叫" << m_name << ",今年" <<
28. m_age << "岁,学号是" <<m_no<<endl;
29. }
30.public:
31. /* 类中的私有成员不能在外部直接访问,但是可
32. * 以提供类似如下的公有成员函数来间接访问,
33. * 在函数中可以对非法数据加以限定控制业务
34. * 逻辑的合理性,这种编程思想就是"封装".*/
35. void setName(const string& newName){
36. if(newName == "二")
37. cout << "你才二" << endl;
38. else
39. m_name = newName;
40. }
41. void setAge(int newAge){
42. if(newAge < 0)
43. cout << "无效年龄" << endl;
44. else
45. m_age = newAge;
46. }
47. void setNo(int newNo){
48. if(newNo < 0)
49. cout << "无效学号" << endl;
50. else
51. m_no = newNo;
52. }
53.private:
54. //属性:成员变量
55. string m_name;
56. int m_age;
57. int m_no;
58.};
59.int main(void){
60. //在栈区创建单个对象
61. //Student s("张飞",25,10011);
62. Student s = Student("张飞",25,10011);
63. s.who();
64.
65. //在栈区创建多个对象
66. Student sarr[3] = {
67. Student("赵云",22,10012),
68. Student("马超",26,10013),
69. Student("刘备",27,10014)};
70. sarr[0].who();
71. sarr[1].who();
72. sarr[2].who();
73.
74. //在堆区创建单个对象
75. Student* ps=new Student("貂蝉",20,10015);
76. ps->who();//(*ps).who();
77. delete ps;
78. ps=NULL;
79.
80. //在堆区创建多个对象,C++11支持
81. Student* parr = new Student[3] {
82. Student("小乔",22,10016),
83. Student("大乔",25,10017),
84. Student("孙尚香",26,10018) };
85. parr[0].who();//(parr+0)->who()
86. parr[1].who();//(parr+1)->who()
87. parr[2].who();//(parr+2)->who()
88.
89. delete[] parr;
90. parr = NULL;
91.
92.
93. return 0;
94.}
5 多文件编程:类的声明和定义可以分别放在不同的文件中
1)类的声明一般放在头文件中(xx.h)
2)类的实现一般放在源文件中(xx.cpp)
//类似qt中的那种写法
十四 构造函数和初始化列表
1 构造函数可以重载,也可以带有缺省参数
//匹配string的无参构造函数
string s;
//匹配string的有参(const char*)构造函数
string s(“hello”);
---------------------------------
http://www.cplusplus.com/
2 缺省构造函数(无参构造函数)
1)如果类中没有定义任何构造函数,编译器会为该类提供一个缺省(无参)构造函数:
–》对于基本类型成员变量不做初始化
–》对于类 类型的成员变量(成员子对象),将会自动调用相应类的无参构造函数来初始化
2)如果自己定义了构造函数,无论是否有参数,那么编译器都不会再提供缺省的无参构造函数了.
3 类型转换构造函数(单参构造函数) //构造函数的类型转换
class 类名{
//可以将源类型变量转换为当前类类型对象.
类名(源类型){…}
};
-----------------------------------
class 类名{
//加“explicit”关键字修饰,可以强制要求这种类型
//转换必须显式的完成.
explicit 类名(源类型){…}
};
4 拷贝构造函数(复制构造函数)
1)用一个已存在的对象作为同类对象的构造实参,创建新的副本对象时,会调用该类拷贝构造函数。
class 类名{
类名(const 类名&){//拷贝构造
…
}
};
------------
eg:
class A{…};
A a1(…);
A a2(a1);//匹配A的拷贝构造函数
2)如果一个类没有自己定义拷贝构造函数,那么编译器会为该类提供一个缺省的拷贝构造函数:
–》对于基本类型的成员变量,按字节复制
–》对于类类型的成员变量(成员子对象),将自动调用相应类的拷贝构造函数来初始化
注:一般不需要自己定义拷贝构造函数函数,因为编译器缺省提供的已经很好用了.
class A1{};//缺省无参,缺省拷贝
class A2{//缺省拷贝
A(void){}
};
class A3{//缺省拷贝
A(int){}
};
class A4{//没有缺省构造
A(const A&){}
};
3)拷贝构造函数调用时机
–》用一个已存在对象作为同类对象的构造实参
–》以对象形式向函数传递参数
–》从函数中返回对象(有可能被编译器优化掉)
1.#include <iostream>
2.using namespace std;
3.class Integer{
4.public:
5. Integer(void){
6. m_i = 0;
7. }
8. //int->Integer
9. /*explicit*/ Integer(int i){
10. cout << "类型转换构造函数" << endl;
11. m_i = i;
12. }
13. void print(void){
14. cout << m_i << endl;
15. }
16.private:
17. int m_i;
18.};
19.
20.
21.int main(void){
22. Integer i;
23. i.print();//0
24. //1)通过类型转换构造函数将100转为Integer对
25. //象,转换结果会保存到一个临时对象中
26. //2)再使用临时对象对i进行赋值操作
27.
28. //在不添加explicit的时候,可以完成隐式转化,添加之后,隐式转换会报错
29. i = 100;
30. i.print();//100
31.
32. //上面代码可读性差,推荐使用下面显式转换
33. //i = (Integer)200;//C风格
34. i = Integer(200);//C++风格
35. i.print();//200
36.
37. return 0;
38.}
1.#include <iostream>
2.using namespace std;
3.class A{
4.public:
5. A(int data = 0){
6. cout << "A(int=0)" << endl;
7. m_data = data;
8. }
9. //拷贝构造函数
10. A(const A& that){
11. cout << "A(const A&)" << endl;
12. //a2.m_data = a1.m_data
13. m_data = that.m_data;
14. }
15. int m_data;
16.};
17.int main(void){
18. const A a1(123);
19. //A a2(a1);
20. A a2 = a1;//和上面等价
21. cout << a1.m_data << endl;//123
22. cout << a2.m_data << endl;//123
23. return 0;
24.}
1.#include <iostream>
2.using namespace std;
3.class A{
4.public:
5. A(int data = 0){
6. cout << "A(int=0)" << endl;
7. m_data = data;
8. }
9. //拷贝构造函数
10. A(const A& that){
11. cout << "A(const A&)" << endl;
12. //a2.m_data = a1.m_data
13. m_data = that.m_data;
14. }
15. int m_data;
16.};
17.class B{
18.public:
19. A m_a;//成员子对象
20.};
21.int main(void){
22. B b1;
23. B b2(b1);//拷贝构造
24. cout << b1.m_a.m_data << endl;//0
25. cout << b2.m_a.m_data << endl;//0
26. return 0;
27.}
1.#include <iostream>
2.using namespace std;
3.class A{
4.public:
5. A(void){
6. cout << "A的无参构造" << endl;
7. }
8. A(const A& that){
9. cout << "A的拷贝构造" << endl;
10. }
11.};
12.void func1(A a){}
13.A func2(void){
14. A a;//无参
15. cout << "&a:" << &a << endl;
16. return a;//拷贝
17.}
18.int main(void){
19. A a1;//无参
20. A a2 = a1;//拷贝
21. func1(a1);//拷贝
22. /* 正常情况func2返回a拷贝到临时对象,临时
23. * 对象在拷贝给a3,发生两次拷贝;现在因为编
24. * 译器优化,让a3直接引用a,不再发生拷贝.
25. * 去优化选项:
26. g++ 10cpCons.cpp -fno-elide-constructors
27. * */
28. A a3 = func2();//拷贝
29. cout << "&a3:" << &a3 << endl;
30. return 0;
31.}