C++之小试牛刀
C到C++的升级
1.变量的定义
C++中所有的变量都可以在使用的时候再定义
C中的变量都必须在作用域开始的位置定义
2.register关键字
C++为了兼容C,所以也支持register关键字,但是在C++的实际编译过程中已经忽略了register关键字,所以得出了与C中的一点不同:在C中对于register修饰的变量不可以取地址,而在C++中对于使用register修饰的变量却是可以取地址操作的
3.定义多个同名的全局变量
在C语言中,重复定义多个同名的全局变量是合法的,会把所有的连接到同一个地址
在C++中,不允许定义多个同名的全局变量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int g_v; 4 5 //int g_v;//C++中不可以定义同名的全局变量 6 7 int main(void) 8 { 9 printf("Begin...\n"); 10 int c = 0; 11 for(int i = 1;i<=3;i++) 12 { 13 for(int j = 1;j<=3;j++) 14 { 15 c += i * j; 16 } 17 } 18 printf("c=%d\n",c); 19 register int a = 0; 20 printf("&a = %p\n",&a); 21 printf("End..\n"); 22 return 0; 23 }
4.struct关键字
C语言中的struct定义了一组变量的集合
C语言中的struct定义的标识符并不是一种新的类型
C++中的struct用于定义一种全新的类型
5.标识符
C++中所有的标识符都必须显示的声明类型,在C中的默认类型在C++中是不合法的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 struct Student 4 { 5 const char* name; 6 int age; 7 }; 8 9 f(i)//error,没有指定返回的类型,以及参数的类型 10 { 11 printf("i = %d\n", i); 12 } 13 14 g()//error,没有指定返回的类型,以及参数的类型 15 { 16 return 5; 17 } 18 19 20 int main(int argc, char *argv[]) 21 { 22 Student s1 = {"Delphi", 30}; 23 Student s2 = {"Tang", 30}; 24 25 f(10); 26 27 printf("g() = %d\n", g(1,2,3,4,5)); 28 29 return 0; 30 }
const关键字
C++在C的基础上对const进行了优化处理
在C++中碰见const声明时在符号表中放入常量,编译过程中若发现使用常量则直接以符号表中的值替换
编译过程中若发现以下情况则给对应的常量分配存储空间:
1.对const常量使用了extern
2.对const常量使用&操作符
注:C++编译器虽然会在上面的两种情况下为const常量分配空间,但是不会使用其存储空间中的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int main() 4 { 5 const int c = 0; 6 int *p = (int*)&c; 7 printf("Begin...\n"); 8 *p = 5; 9 printf("c=%d\n",c);//c的值为0 10 printf("End...\n"); 11 return 0; 12 13 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 void f() 4 { 5 #define a 3//在预处理期间就完成了文本的替换,所以没有作用域的限制 6 const int b = 4; 7 } 8 9 void g() 10 { 11 printf("a = %d\n", a); 12 //printf("b = %d\n", b); 13 } 14 15 int main() 16 { 17 const int A = 1; 18 const int B = 2; 19 int array[A + B] = {0};//在C编译器中,因为const修饰的变量未只读变量,在运行中才可以知道数组的大小故会报错而在C++编译器中const修饰的变量是真正意义上的常量,A和B直接使用符号表中的变量替换 20 int i = 0; 21 22 for(i=0; i<(A + B); i++) 23 { 24 printf("array[%d] = %d\n", i, array[i]); 25 } 26 printf("a = %d\n", a); 27 f(); 28 g(); 29 30 return 0; 31 }
布尔类型和引用
C++在C语言基本类型系统的基础上增加了bool(布尔)类型
C++中的bool可取的值只有true(非0)和false(0)这两个值,
C++编译器会把非0值转换为true,0值转换为false
理论上bool只占用一个字节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int main(int argc, char *argv[]) 4 { 5 bool b = false; 6 int a = b; 7 8 printf("sizeof(b) = %d\n", sizeof(b)); 9 printf("b = %d, a = %d\n", b, a); 10 11 b = 3; 12 a = b; 13 14 printf("b = %d, a = %d\n", b, a); 15 16 b = -5; 17 a = b; 18 19 printf("b = %d, a = %d\n", b, a); 20 21 a = 10; 22 b = a; 23 24 printf("a = %d, b = %d\n", a, b); 25 26 a = 0; 27 b = a; 28 29 printf("a = %d, b = %d\n", a, b); 30 31 return 0; 32 }
三目运算符
C语言中的三目运算符返回的是变量值,故不可以作为左值使用
C++中的三目运算符可直接返回变量本身(返回的是变量的引用),故既可以作为左值又可以作为右值使用
注:三目运算符返回的值中如果有一个是常量值,则与C语言中的用法一样,返回的是变量的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int main(int argc, char *argv[]) 4 { 5 int a = 1; 6 int b = 2; 7 (a<b?a:b) = 3; 8 printf("a = %d,b = %d\n",a,b); 9 10 return 0; 11 }
引用
1.引用可以看成是一个已经定义的变量的别名
2.语法:Type &name = var;
注:普通引用在定义时必须用同类型的变量进行初始化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int main(int argc, char *argv[]) 4 { 5 int a = 4; 6 int& b = a; 7 8 b = 5; 9 10 printf("a = %d\n", a); 11 printf("b = %d\n", b); 12 printf("&a = %p\n", &a); 13 printf("&b = %p\n", &b); 14 15 return 0; 16 }
3.引用作为变量别名的存在,因此在一些场合可以代替指针
4.引用相对于指针来说具有更好的可读性和实用性
注:函数中的引用形参不需要进行初始化
1 void swap(int &a,int &b) 2 { 3 int t = a; 4 a = b; 5 b = t; 6 } 7 8 void swap(int *a,int *b) 9 { 10 int t = *a; 11 *a = *b; 12 *b = t; 13 }
const引用
1.语法:const Type & name = var
2.const引用让变量拥有只读属性
1 int a = 4; 2 const int& b =a; 3 int *p = (int*)&b; 4 b = 5;//error,b为只读变量 5 *p = 5;//ok,修改变量a的值
3.当使用常量对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
1 const int& b = 1; 2 int* p = (int*)&b; 3 b = 5;//error,只读变量 4 *p = 5;//ok,修改变量a的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 void Example() 4 { 5 printf("Example:\n"); 6 7 int a = 4; 8 const int& b = a; 9 int* p = (int*)&b; 10 11 //b = 5; 12 13 *p = 5; 14 15 printf("a = %d\n", a); 16 printf("b = %d\n", b); 17 } 18 19 void Demo() 20 { 21 printf("Demo:\n"); 22 23 const int& c = 1; 24 int* p = (int*)&c; 25 26 //c = 5; 27 28 *p = 5; 29 30 printf("c = %d\n", c); 31 } 32 33 int main(int argc, char *argv[]) 34 { 35 Example(); 36 37 printf("\n"); 38 39 Demo(); 40 41 return 0; 42 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 struct TRef 4 { 5 char& r; 6 }; 7 8 int main(int argc, char *argv[]) 9 { 10 char c = 'c'; 11 char& rc = c; 12 TRef ref = { c }; 13 14 printf("sizeof(char&) = %d\n", sizeof(char&));//引用就是变量的别名,在此处就是变量本身(即为变量c) 15 printf("sizeof(rc) = %d\n", sizeof(rc));//相当于sizeof(c) 16 17 printf("sizeof(TRef) = %d\n", sizeof(TRef)); 18 printf("sizeof(ref.r) = %d\n", sizeof(ref.r));//相当于sizeof(c) 19 20 return 0; 21 }
引用的本质
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 struct TRef 4 { 5 char* before; 6 char& ref; 7 char* after; 8 }; 9 10 int main(int argc, char* argv[]) 11 { 12 char a = 'a'; 13 char& b = a; 14 char c = 'c'; 15 16 TRef r = {&a, b, &c}; 17 18 printf("sizeof(r) = %d\n", sizeof(r)); 19 printf("sizeof(r.before) = %d\n", sizeof(r.before)); 20 printf("sizeof(r.after) = %d\n", sizeof(r.after)); 21 printf("&r.before = %p\n", &r.before); 22 printf("&r.after = %p\n", &r.after); 23 24 return 0; 25 }
使用引用的意义
1.功能性:可以满足多数需要使用指针的场合
2.安全性:可以避开由于指针操作不当而带来的内存错误
3.操作性:简单易用,有不失功能强大
4.引用的注意点如下
1 #include <stdio.h> 2 3 int& demo() 4 { 5 int d = 0; 6 7 printf("demo: d = %d\n", d); 8 9 return d;//此处有问题,引用与指针一样不可以返回局部变量的引用,这样就相当于是野指针 10 } 11 12 int& func() 13 { 14 static int s = 0; 15 16 printf("func: s = %d\n", s); 17 18 return s; 19 } 20 21 int main(int argc, char* argv[]) 22 { 23 int& rd = demo(); 24 int& rs = func(); 25 26 printf("\n"); 27 printf("main: rd = %d\n", rd); 28 printf("main: rs = %d\n", rs); 29 printf("\n"); 30 31 rd = 10; 32 rs = 11; 33 34 demo(); 35 func(); 36 37 printf("\n"); 38 printf("main: rd = %d\n", rd); 39 printf("main: rs = %d\n", rs); 40 printf("\n"); 41 42 return 0; 43 }
内联函数分析
1.C++中使用内联函数替代宏代码片段
2.C++使用inline关键字声明内联函数
格式:
inline int func(int a,int b)
{
return a<b?a:b;
}
注:内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求
3.C++中编译器直接将内联函数的函数体插入到函数调用的地方
4.内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)
5.C++编译器不一定满足函数的内联请求(不满足时此时相当于是普通函数)
内联函数的优势:
1.内联函数具有普通函数的特征,会进行语法和语义的检查,而宏代码段只是进行简单的文本替换而不会有语法和语义的检查
如何进行强制内联
g++:使用__attribute__((always_inline))属性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 4 __attribute__((always_inline))//强制内联属性 5 6 int add_inline(int n); 7 8 int main(int argc, char *argv[]) 9 { 10 int r = add_inline(10); 11 12 printf(" r = %d\n", r); 13 14 return 0; 15 } 16 17 inline int add_inline(int n) 18 { 19 int ret = 0; 20 21 for(int i=0; i<n; i++) 22 { 23 ret += i; 24 } 25 26 return ret; 27 }
内联编译的限制:
1.不能存在任何形式的循环语句
2.不能存在过多的条件判断语句
3.函数体不能过于庞大
4.不能对函数进行取址操作
5.函数的内联声明必须在调用语句之前
C++对于函数的参数的扩展
1.C++中可以在函数声明的时候为参数提供一个默认值,当函数调用没有提供参数的值的时候,则使用默认值
注:参数的默认值必须在函数声明中指定,在定义种参数不可以出现默认值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int mul(int x = 0); 4 5 int main(int argc, char *argv[]) 6 { 7 printf("%d\n", mul()); 8 printf("%d\n", mul(-1)); 9 printf("%d\n", mul(2)); 10 11 return 0; 12 } 13 14 int mul(int x) 15 { 16 return x * x; 17 }
2.参数的默认值必须从右到左提供
3.函数调用的时候是从左到右匹配的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int add(int x, int y = 0, int z = 0); 4 5 int main(int argc, char *argv[]) 6 { 7 printf("%d\n", add(1)); 8 printf("%d\n", add(1, 2)); 9 printf("%d\n", add(1, 2, 3)); 10 11 return 0; 12 } 13 14 int add(int x, int y, int z) 15 { 16 return x + y + z; 17 }
函数占位参数
1.在C++中可以为函数提供占位参数,占位参数只有参数类型的声明,而没有参数名的声明,一般情况下,在函数体的内部无法使用占位参数
2.意义:
1)占位参数与默认参数结合起来使用
2)兼容C语言程序中可能出现的不规范写法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int func(int x, int = 0); 4 5 int main(int argc, char *argv[]) 6 { 7 printf("%d\n", func(1)); 8 printf("%d\n", func(2, 3)); 9 10 return 0; 11 } 12 13 int func(int x, int) 14 { 15 return x; 16 }
函数重载
概念:同一个标识符在不同的上下文中有不同的意义
实现:使用同一个函数名定义不同的函数,同一个函数名和不同的参数搭配的时候代表的函数含义是不同的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <string.h> 3 4 int func(int x) 5 { 6 return x; 7 } 8 int func(int a,int b) 9 { 10 return a + b; 11 } 12 int func(const char*s) 13 { 14 return strlen(s); 15 } 16 int main(void) 17 { 18 19 printf("%d\n",func(3)); 20 printf("%d\n",func(4,5)); 21 printf("%d\n",func("D.T.Software")); 22 return 0; 23 }
重载至少满足的条件
1.参数个数不同
2.参数类型不同
3.参数顺序不同
注:默认参数遇上函数重载,会发生不知道调用哪个函数的情况
例如:
int func(int a,int b,int c = 0)
{
return a * b * c;
}
int func(int a,int b)
{
return a + b;
}
int main(void)
{
int c = func(1,2);//此处不知道调用哪个函数
return 0;
}
函数重载的实质
1.重载函数在本质上是相互独立的函数
2.重载函数的类型是不同
3.函数重载必须发生在同一个作用域
注:函数的返回值不可以作为重载的依据,函数重载是由函数名和参数列表决定的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 int add(int a, int b) // int(int, int) 4 { 5 return a + b; 6 } 7 8 int add(int a, int b, int c) // int(int, int, int) 9 { 10 return a + b + c; 11 } 12 13 int main() 14 { 15 printf("%p\n", (int(*)(int, int))add); 16 printf("%p\n", (int(*)(int, int, int))add); 17 18 return 0; 19 }
函数重载与函数指针
函数指针选择重载函数的原则
1.函数指针根据参数列表选择和重载函数一致的候选者
2.严格匹配候选者的函数类型与函数指针的函数类型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <string.h> 3 4 int func(int x) 5 { 6 return x; 7 } 8 9 int func(int a, int b) 10 { 11 return a + b; 12 } 13 14 int func(const char* s) 15 { 16 return strlen(s); 17 } 18 19 typedef int(*PFUNC)(int a); 20 21 22 int main(int argc, char *argv[]) 23 { 24 int c = 0; 25 26 PFUNC p = func; 27 28 c = p(1); 29 30 printf("c = %d\n", c); 31 32 return 0; 33 }
注:1.函数重载必然发生在同一个作用域中
2.编译器需要用参数列表或函数类型进行参数选择
C++中调用C
使用C++编译器内置的标准宏定义__cplusplus
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "add.h" 2 int add(int a, int b) 3 4 { 5 6 return a + b; 7 8 } 9 add.c 10 11 int add(int a, int b); 12 add.h 13 14 #include <stdio.h> 15 16 17 #ifdef __cplusplus 18 extern "C" { 19 #endif 20 21 #include "add.h" 22 23 #ifdef __cplusplus 24 } 25 #endif 26 27 28 int main() 29 { 30 int c = add(1, 2); 31 32 printf("c = %d\n", c); 33 34 return 0; 35 } 36 37 main.cpp
动态内存分配
1.C++中的内存分配是通过new关键字进行的
2.C++中的动态内存的申请是基于类型进行的
3.delete关键字用于内存的释放
4.格式:
变量的申请
Type *pointer = new Type;
delete pointer;
数组的申请
Type * pointer = new Type[N];
delete[] pointer;
Type:代表类型
N:代表个数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> int main() { int* p = new int; *p = 5; *p = *p + 10; printf("p = %p\n", p); printf("*p = %d\n", *p); delete p; p = new int[10]; for(int i=0; i<10; i++) { p[i] = i + 1; printf("p[%d] = %d\n", i, p[i]); } delete[] p; return 0; }
new与malloc的区别
new关键字是C++的一部分
malloc是由C库提供的函数
new是以具体类型为单位进行内存分配
malloc是以字节为单位进行内存分配的
new在申请单个类型变量时可进行初始化
malloc不具备内存初始化的特性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> int main() { int* pi = new int(1); float* pf = new float(2.0f); char* pc = new char('c'); printf("*pi = %d\n", *pi); printf("*pf = %f\n", *pf); printf("*pc = %c\n", *pc); delete pi; delete pf; delete pc; return 0; }
命名空间
C++命名空间的定义:
namespace Name
{
namespace Internal
{
}
}
使用:
使用整个命名空间:using namespace name;
使用命名空间中的变量:using name::variable;
使用默认命名空间中的变量:::variable;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> namespace First { int i = 0; } namespace Second { int i = 1; namespace Internal { struct P { int x; int y; }; } } int main() { using namespace First; using Second::Internal::P; printf("First::i = %d\n", i); printf("Second::i = %d\n", Second::i); P p = {2, 3}; printf("p.x = %d\n", p.x); printf("p.y = %d\n", p.y); return 0; }
强制类型转换
C语言中的强制类型转换的缺陷
1.过于粗暴
任意类型之间都可以进行转换,编译器很难判断其正确性
2.难于定位
在源码中无法快速定位所有使用强制类型转换的语句
static_cast
1.用于基本类型间的转换
2.不能用于基本类型指针间的转换
3.用于有继承关系类对象之间的转换和类指正之间的转换
4.格式:static_cast<Type>(Expression)
const_cast
1.用于除去变量的只读属性
2.强制转换的目标类型必须是指针或引用
3.格式:const_cast<Type>(Expression)
reinterpret_cast
1.用于指针类型间的强制转换
2.用于整数和指针类型间的强制转换(目标类型必须是指针类型)
3.格式:reinterpret_cast<Type>(Expression)
dynamic_cast
1.用于有继承关系的类指针间的转换
2.用于有交叉关系的类指针间的转换
3.具有类型检查的功能
4.需要虚函数的支持
5.格式:dynamic_cast<Type>(Expression)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> void static_cast_demo() { int i = 0x12345; char c = 'c'; int* pi = &i; char* pc = &c; c = static_cast<char>(i); // pc = static_cast<char*>(pi);//error 不可以用于基本类指针之间的转换 } void const_cast_demo() { const int& j = 1; int& k = const_cast<int&>(j); const int x = 2; int& y = const_cast<int&>(x); // int z = const_cast<int>(x);//error 用于去除只读属性,目标类型必须是指针或者引用 k = 5; printf("k = %d\n", k); printf("j = %d\n", j); y = 8; printf("x = %d\n", x); printf("y = %d\n", y); printf("&x = %p\n", &x); printf("&y = %p\n", &y); } void reinterpret_cast_demo() { int i = 0; char c = 'c'; int* pi = &i; char* pc = &c; pc = reinterpret_cast<char*>(pi); pi = reinterpret_cast<int*>(pc); pi = reinterpret_cast<int*>(i); // c = reinterpret_cast<char>(i); //用于指针之间的转换和与指针之间的类型转换(用于此功能目标类型必须是指针) } void dynamic_cast_demo() { int i = 0; int* pi = &i; // char* pc = dynamic_cast<char*>(pi); } int main() { static_cast_demo(); const_cast_demo(); reinterpret_cast_demo(); dynamic_cast_demo(); return 0; }
const什么时候是只读变量,什么时候是常量
const常量的判别标准
1.只有使用字面量初始化的const常量才会进入符号表
2.使用其他变量初始化的const常量仍然是只读变量
3.被volatile修饰的const常量不会进入符号表
注:在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理
const引用的类型与初始化变量的类型
相同:初始化变量成为只读变量
不同:生成一个新的只读变量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> int main() { const int x = 1; const int& rx = x; int& nrx = const_cast<int&>(rx); nrx = 5; printf("x = %d\n", x); printf("rx = %d\n", rx); printf("nrx = %d\n", nrx); printf("&x = %p\n", &x); printf("&rx = %p\n", &rx); printf("&nrx = %p\n", &nrx); volatile const int y = 2; int* p = const_cast<int*>(&y); *p = 6; printf("y = %d\n", y); printf("p = %p\n", p); const int z = y; p = const_cast<int*>(&z); *p = 7; printf("z = %d\n", z); printf("p = %p\n", p); char c = 'c'; char& rc = c; const int& trc = c;//生成新的只读变量,与rc没有关系 rc = 'a'; printf("c = %c\n", c); printf("rc = %c\n", rc); printf("trc = %c\n", trc); printf("c = %p\n", &c); printf("rc = %p\n", &rc); printf("trc = %p\n", &trc); return 0; }
运行结果:
引用与指针之间的关系
指针是一个变量
1.值为一个内存地址,不需要初始化,可以保存不同的地址
2.通过指针可以访问对应内存中的值
3.指针可以被const修饰成为常量或者只读变量
引用只是一个变量的新名字
1.对引用的操作(赋值,取值等)都会传递到代表的变量上
2.const引用时代表的变量具有只读属性
3.引用必须在定义时初始化,之后无法代表其他的变量
没有数组的引用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> int a = 1; struct SV { int& x; int& y; int& z; }; int main() { int b = 2; int* pc = new int(3); SV sv = {a, b, *pc}; // int& array[] = {a, b, *pc}; // &array[1] - &array[0] = ? Expected ==> 4,数组存放是一片连续的地址 printf("&sv.x = %p\n", &sv.x); printf("&sv.y = %p\n", &sv.y); printf("&sv.z = %p\n", &sv.z); delete pc; return 0; }
类与封装的概念
C++中类的封装
成员变量:C++中用于表示类属性的变量
成员函数:C++中用于表示类行为的函数
C++中可以给成员变量和成员函数定义访问级别
public:成员变量和成员函数可以在类的内部和外部访问和调用
private:成员变量和成员函数只能在类的内部被访问和调用
注:类的封装机制使得使用方式和内部细节相分离
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <stdio.h> struct Biology { bool living; }; struct Animal : Biology { bool movable; void findFood() { } }; struct Plant : Biology { bool growable; }; struct Beast : Animal { void sleep() { } }; struct Human : Animal { void sleep() { printf("I'm sleeping...\n"); } void work() { printf("I'm working...\n"); } }; struct Girl : Human { private: int age; int weight; public: void print() { age = 22; weight = 48; printf("I'm a girl, I'm %d years old.\n", age); printf("My weight is %d kg.\n", weight); } }; struct Boy : Human { private: int height; int salary; public: int age; int weight; void print() { height = 175; salary = 9000; printf("I'm a boy, my height is %d cm.\n", height); printf("My salary is %d RMB.\n", salary); } }; int main() { Girl g; Boy b; g.print(); b.age = 19; b.weight = 120; //b.height = 180; b.print(); return 0; }
类成员的作用域
1.类成员的作用域都只是在类的内部,外部无法直接访问
2.类成员函数可以直接访问成员变量和调用成员函数
3.类的外部可以通过类变量访问public成员
4.类成员的作用域与访问级别没有关系
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> int i = 1; struct Test { private: int i; public: int j; int getI() { i = 3; return i; } }; int main() { int i = 2; Test test; test.j = 4; printf("i = %d\n", i); // i = 2; printf("::i = %d\n", ::i); // ::i = 1; // printf("test.i = %d\n", test.i); // Error printf("test.j = %d\n", test.j); // test.j = 4 printf("test.getI() = %d\n", test.getI()); // test.getI() = 3 return 0; }
类的关键字struct和class的区别
使用struct定义类时,所有成员的默认访问级别为public
使用class定义类时,所有的成员默认访问级别为private
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> struct A { // defualt to public int i; // defualt to public int getI() { return i; } }; class B { // defualt to private int i; // defualt to private int getI() { return i; } }; int main() { A a; B b; a.i = 4; printf("a.getI() = %d\n", a.getI()); b.i = 4;//由于默认是private,所以错误 printf("b.getI() = %d\n", b.getI());//由于默认是private,所以错误 return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#ifndef _OPERATOR_H_ #define _OPERATOR_H_ class Operator { private: char mOp; double mP1; double mP2; public: bool setOperator(char op); void setParameter(double p1, double p2); bool result(double& r); }; #endif operator.h #include "Operator.h" bool Operator::setOperator(char op) { bool ret = false; if( (op == '+') || (op == '-') || (op == '*') || (op == '/') ) { ret = true; mOp = op; } else { mOp = '\0'; } return ret; } void Operator::setParameter(double p1, double p2) { mP1 = p1; mP2 = p2; } bool Operator::result(double& r) { bool ret = true; switch( mOp ) { case '/': if( (-0.000000001 < mP2) && (mP2 < 0.000000001) ) { ret = false; } else { r = mP1 / mP2; } break; case '+': r = mP1 + mP2; break; case '*': r = mP1 * mP2; break; case '-': r = mP1 - mP2; break; default: ret = false; break; } return ret; } operator.cpp #include <stdio.h> #include "Operator.h" int main() { Operator op; double r = 0; op.setOperator('/'); op.setParameter(9, 3); if( op.result(r) ) { printf("r = %lf\n", r); } else { printf("Calculate error!\n"); } return 0; }
构造函数
1.构造函数名与类名相同
2.构造函数无任何返回类型的声明
3.构造函数在对象定义的时候自动被调用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: int i; int j; public: int getI(){return i;} int getJ(){return j;} Test() { i = 1; j = 2; } }; Test gt; int main(void) { printf("gt.i = %d\n",gt.getI()); printf("gt.j = %d\n",gt.getJ()); Test tl; printf("tl.i = %d\n",tl.getI()); printf("tl.j = %d\n",tl.getJ()); Test* pt = new Test; printf("pt->i = %d\n",pt->getI()); printf("pt->j = %d\n",pt->getJ()); delete pt; return 0; }
4.带参数的构造函数(类似于函数重载,遵循函数重载的原则)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { public: Test() { printf("Test()\n"); } Test(int v) { printf("Test(int v)\n"); } }; int main(void) { Test t; Test t1(1); Test t2 = 2; int i(100); printf("i = %d\n",i); return 0; }
5.构造函数的调用
1)一般情况下,构造函数在对象定义时被自动调用
2)一些特殊情况下,需要手工调用构造函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: int m_value; public: Test() { printf("Test()\n"); m_value = 0; } Test(int v) { printf("Test(int v),v = %d\n",v); m_value = v; } int getValue() { return m_value; } }; int main(void) { Test ta[3] = {Test(),Test(1),Test(2)}; for(int i = 0;i<3;i++) { printf("ta[%d].getValue() = %d\n",i,ta[i].getValue()); } Test t = Test(100); printf("t.getValue() = %d\n",t.getValue()); return 0; }
6.两个特殊的构造函数
1)无参构造函数
.当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
2)拷贝构造函数
.当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,简单的进行成员变量的值的复制
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } Test(const Test& t) { i = t.i; j = t.j; } Test() { } }; int main() { Test t1; Test t2 = t1; printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ()); printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ()); return 0; }
7.拷贝函数的意义
.浅拷贝:拷贝对象的物理状态
.深拷贝:拷贝对象的逻辑状态
注:编译器提供的拷贝函数只进行浅拷贝
8.什么时候进行深拷贝
--对象中有成员指代了系统中的资源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: int i; int j; int* p; public: int getI() { return i; } int getJ() { return j; } int* getP() { return p; } Test(const Test& t) { i = t.i; j = t.j; p = new int; *p = *t.p; } Test(int v) { i = 1; j = 2; p = new int; *p = v; } void free() { delete p; } }; int main() { Test t1(3); Test t2(t1); printf("t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), *(t1.getP())); printf("t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), *(t2.getP())); t1.free(); t2.free(); return 0; }
9.拷贝构造函数容易出现对一块空间的连续释放的问题
注:1.一般原则,自定义拷贝构造函数,必然需要实现深拷贝
初始化列表的使用
C++中提供了初始化列表对成员变量进行初始化
语法:ClassName::ClassName():m1(v1),m2(v2),m3(v3)
{
}
类成员的初始化
1.成员的初始化顺序与成员的声明顺序相同
2.成员的初始化顺序与初始化列表中的位置无关
3.初始化列表先于构造函数的函数体执行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Value { private: int mi; public: Value(int i) { printf("i = %d\n", i); mi = i; } int getI() { return mi; } }; class Test { private: Value m2; Value m3; Value m1; public: Test() : m1(1), m2(2), m3(3) { printf("Test::Test()\n"); } }; int main() { Test t; return 0; }
结果为:
类中的const成员
1.类中的const成员会被分配空间
2.类中的const成员的本质是只读变量
3.类中的const成员只能在初始化列表中指定初始值
注:编译器无法直接得到const成员的初始值,因此无法进入符号表成为真正意义上的常量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Value { private: int mi; public: Value(int i) { printf("i = %d\n", i); mi = i; } int getI() { return mi; } }; class Test { private: const int ci; Value m2; Value m3; Value m1; public: Test() : m1(1), m2(2), m3(3), ci(100) { printf("Test::Test()\n"); } int getCI() { return ci; } int setCI(int v) { int* p = const_cast<int*>(&ci); *p = v; return 0; } }; int main() { Test t; printf("t.ci = %d\n", t.getCI()); t.setCI(10); printf("t.ci = %d\n", t.getCI()); return 0; }
初始化与赋值不同
初始化:对正在创建的对象进行初值设置
int(100);等同int i = 100;
赋值:对已经存在的对象进行值的设置
int i;i = 100;
对象的构造顺序
局部对象:当程序执行到达对象定义语句时进行构造
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: int mi; public: Test(int i) { mi = i; printf("Test(int i):%d\n",mi); } Test(const Test& obj) { mi = obj.mi; printf("Test(const Test& obj):%d\n",mi); } }; int main(void) { int i = 0; Test a1 = i; while(i<3) { Test a2 = ++i; } if(i<4) { Test a = a1; } else { Test a(100); } return 0; }
堆对象:当程序执行流到达new语句时创建对象
使用new创建对象将自动触发构造函数的调用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: int mi; public: Test(int i) { mi = i; printf("Test(int i): %d\n", mi); } Test(const Test& obj) { mi = obj.mi; printf("Test(const Test& obj): %d\n", mi); } int getMi() { return mi; } }; int main() { int i = 0; Test* a1 = new Test(i); // Test(int i): 0 while( ++i < 10 ) if( i % 2 ) new Test(i); // Test(int i): 1, 3, 5, 7, 9 if( i < 4 ) new Test(*a1); else new Test(100); // Test(int i): 100 return 0; }
注:全局对象的构造顺序不确定,这个有编译器决定,不同编译器构造顺序不同
析构函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { int mi; public: Test(int i) { mi = i; printf("Test(): %d\n", mi); } ~Test() { printf("~Test(): %d\n", mi); } }; int main() { Test t(1); Test* pt = new Test(2); delete pt; return 0; }
自己定义析构函数的准则
当类中自定义了构造函数,并且构造函数中使用了系统资源(如:内存申请,文件打开等)则需要自己定义析构函数
神秘的临时对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { int mi; public: Test(int i) { mi = i; } Test() { Test(0); } void print() { printf("mi = %d\n", mi); } }; int main() { Test t; t.print(); return 0; }
程序意图:
在Test()中以0作为参数调用Test(int i)
将成员变量mi的初始值设置为0
运行结果:
成员变量mi的值为随机值
原因:
1.直接调用构造函数将产生一个临时对象
2.临时对象的生命周期只有一条语句的时间
3.临时对象的作用域只在一条语句中
4.临时对象是C++中值得警惕的灰色地带
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { int mi; void init(int i) { mi = i; } public: Test(int i) { init(i); } Test() { init(0); } void print() { printf("mi = %d\n", mi); } }; int main() { Test t; t.print(); return 0; }
C++编译器会将少临时对象的产生
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { int mi; public: Test(int i) { printf("Test(int i) : %d\n", i); mi = i; } Test(const Test& t) { printf("Test(const Test& t) : %d\n", t.mi); mi = t.mi; } Test() { printf("Test()\n"); mi = 0; } int print() { printf("mi = %d\n", mi); return 0; } ~Test() { printf("~Test()\n"); } }; Test func() { return Test(20); } int main() { Test t = Test(10); // ==> Test t = 10;应该是1.生成临时对象2.使用临时对象来初始化t对象此时会调用拷贝构造函数,但是由于编译器会减少临时对象的产生故在此处相当于前一句话 Test tt = func(); // ==> Test tt = Test(20); ==> Test tt = 20; t.print(); tt.print(); return 0; }
注:c++中临时对象和野指针是bug的主要来源
单个对象创建时构造函数的调用顺序
1.调用父类的构造过程
2.调用成员变量的构造函数(调用顺序与声明的顺序相同)
3.调用类自身的构造函数
对于栈对象和全局对象,类似于入栈与出栈的顺序,最后构造的对象最先被析构
对于堆对象析构的发生于delete的使用顺序有关
析构函数与构造函数的顺序相反
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Member { const char* ms; public: Member(const char* s) { printf("Member(const char* s): %s\n", s); ms = s; } ~Member() { printf("~Member(): %s\n", ms); } }; class Test { Member mA; Member mB; public: Test() : mB("mB"), mA("mA") { printf("Test()\n"); } ~Test() { printf("~Test()\n"); } }; Member gA("gA"); int main() { Test t; return 0; }
const对象
1.const关键字可以修饰对象
2.coant修饰的对象为只读对象
3.只读对象的成员变量不允许被改变
4.只读对象是编译阶段的概念,运行时无效
cosnt成员函数
1.const对象只能调用const的成员函数
2.const成员函数只能调用const成员函数
3.const成员函数中不能直接改写成员变量的值
const成员函数的定义
Type ClassName::function(Type p) const
类中的函数声明与实际函数定义中都必须带const关键字
类中成员的归属
1.每一个对象拥有自己对应的属性(所以成员变量不共享)
2.所有的对象共享类的方法(成员函数)
3.方法能够直接访问对象的属性
4.方法中的隐藏参数this用于指代当前对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { int mi; public: int mj; Test(int i); Test(const Test& t); int getMi(); void print(); }; Test::Test(int i) { mi = i; } Test::Test(const Test& t) { mi = t.mi; } int Test::getMi() { return mi; } void Test::print() { printf("this = %p\n", this); } int main() { Test t1(1); Test t2(2); Test t3(3); printf("t1.getMi() = %d\n", t1.getMi()); printf("&t1 = %p\n", &t1); t1.print(); printf("t2.getMi() = %d\n", t2.getMi()); printf("&t2 = %p\n", &t2); t2.print(); printf("t3.getMi() = %d\n", t3.getMi()); printf("&t3 = %p\n", &t3); t3.print(); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: int mCount; public: Test() : mCount(0) { mCount++; } ~Test() { --mCount; } int getCount() { return mCount; } }; Test gTest; int main() { Test t1; Test t2; printf("count = %d\n", gTest.getCount()); printf("count = %d\n", t1.getCount()); printf("count = %d\n", t2.getCount()); return 0; }
运行结果:
C++定义静态成员变量
1.静态成员变量属于整个类所有
2.静态成员变量的生命周期不依赖于任何对象
3.可以通过类名直接访问共有静态成员变量
4.所有对象共享类的静态成员变量
5.可以通过对象名访问公有静态成员变量
6.在定义时直接通过static关键字修饰
7.静态成员变量在程序内部位于全局数据区
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { private: static int cCount; public: Test() { cCount++; } ~Test() { --cCount; } int getCount() { return cCount; } }; int Test::cCount = 0; Test gTest; int main() { Test t1; Test t2; printf("count = %d\n", gTest.getCount()); printf("count = %d\n", t1.getCount()); printf("count = %d\n", t2.getCount()); Test* pt = new Test(); printf("count = %d\n", pt->getCount()); delete pt; printf("count = %d\n", gTest.getCount()); return 0; }
C++中静态成员函数
1.静态成员函数属于整个类所有
2.可以通过类名直接访问公有静态成员函数
3.可以通过对象名访问公有静态成员函数
半成品对象
初始化操作不能按照预期完成而得到的对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { int mi; int mj; bool mStatus; public: Test(int i, int j) : mStatus(false) { mi = i; return; mj = j; mStatus = true; } int getI() { return mi; } int getJ() { return mj; } int status() { return mStatus; } }; int main() { Test t1(1, 2); if( t1.status() ) { printf("t1.mi = %d\n", t1.getI()); printf("t1.mj = %d\n", t1.getJ()); } return 0; }
二阶构造
1.资源无关的初始化操作
不可能出现异常情况的操作
2.需要使用资源的操作
可能出现异常情况,如:内存申请,访问文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class TwoPhaseCons { private: TwoPhaseCons() // 第一阶段构造函数 { } bool construct() // 第二阶段构造函数 { return true; } public: static TwoPhaseCons* NewInstance(); // 对象创建函数 }; TwoPhaseCons* TwoPhaseCons::NewInstance() { TwoPhaseCons* ret = new TwoPhaseCons(); // 若第二阶段构造失败,返回 NULL if( !(ret && ret->construct()) ) { delete ret; ret = NULL; } return ret; } int main() { TwoPhaseCons* obj = TwoPhaseCons::NewInstance(); printf("obj = %p\n", obj); delete obj; return 0; }
友元
1.友元是C++中的一种关系
2.友元发生在函数与类之间或者类与类之间
3.友元关系是单项的,不能传递
4.在类中以friend关键字声明友元
5.类的友元可以是其他类或者具体函数
6.友元不受类中访问级别的限制
7.友元可以直接访问具体类的所有成员
8.友元不是类的一部分
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <math.h> class Point { double x; double y; public: Point(double x, double y) { this->x = x; this->y = y; } double getX() { return x; } double getY() { return y; } friend double func(Point& p1, Point& p2); }; double func(Point& p1, Point& p2) { double ret = 0; ret = (p2.y - p1.y) * (p2.y - p1.y) + (p2.x - p1.x) * (p2.x - p1.x); ret = sqrt(ret); return ret; } int main() { Point p1(1, 2); Point p2(10, 20); printf("p1(%f, %f)\n", p1.getX(), p1.getY()); printf("p2(%f, %f)\n", p2.getX(), p2.getY()); printf("|(p1, p2)| = %f\n", func(p1, p2)); return 0; }
友元的尴尬
1.友元是为了兼顾C语言的高效而诞生的
2.友元直接破坏了面向对象的封装性
3.友元在实际产品中的高效是得不偿失的
4.友元在现代软件工程中已经逐渐被遗弃
注意:
1.友元关系不具备传递性
2.类的友元可以是其他类的成员函数
3.类的友元可以是某个完整的类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class ClassC { const char* n; public: ClassC(const char* n) { this->n = n; } friend class ClassB; }; class ClassB { const char* n; public: ClassB(const char* n) { this->n = n; } void getClassCName(ClassC& c) { printf("c.n = %s\n", c.n); } friend class ClassA; }; class ClassA { const char* n; public: ClassA(const char* n) { this->n = n; } void getClassBName(ClassB& b) { printf("b.n = %s\n", b.n); } /* void getClassCName(ClassC& c)//由于友元不具有传递性所以此处的语句错误 { printf("c.n = %s\n", c.n); } */ }; int main() { ClassA A("A"); ClassB B("B"); ClassC C("C"); A.getClassBName(B); B.getClassCName(C); return 0; }
类中函数的重载
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Test { int i; public: Test() { printf("Test::Test()\n"); this->i = 0; } Test(int i) { printf("Test::Test(int i)\n"); this->i = i; } Test(const Test& obj) { printf("Test(const Test& obj)\n"); this->i = obj.i; } static void func() { printf("void Test::func()\n"); } void func(int i) { printf("void Test::func(int i), i = %d\n", i); } int getI() { return i; } }; void func() { printf("void func()\n"); } void func(int i) { printf("void func(int i), i = %d\n", i); } int main() { func(); func(1); Test t; // Test::Test() Test t1(1); // Test::Test(int i) Test t2(t1); // Test(const Test& obj) func(); // void func() Test::func(); // void Test::func() func(2); // void func(int i), i = 2; t1.func(2); // void Test::func(int i), i = 2 t1.func(); // void Test::func() return 0; }
重载的意义
1.通过函数名对函数功能进行提示
2.通过参数列表对函数用法进行提示
3.扩展系统中已经存在的函数功能
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <string.h> char* strcpy(char* buf, const char* str, unsigned int n) { return strncpy(buf, str, n); } int main() { const char* s = "D.T.Software"; char buf[8] = {0}; //strcpy(buf, s); strcpy(buf, s, sizeof(buf)-1); char buf1[20] = {0}; strcpy(buf1,s); printf("%s\n", buf); printf("%s\n", buf1); return 0; }
操作符的重载
1.使用operator关键字进行定义特殊的函数
2.operator的本质是通过函数重载操作符
3.语法:
Type operator sign(const Type& p1,const Type& p2)
{
Type ret;
return ret;
}
sign为系统中定义的操作符,如:+,-,*,/等
将操作符重载函数定义为类的成员函数
1.比全局操作符重载函数少一个参数(左操作数)
2.不需要依赖友元就可以完成操作符重载
3.编译器优先在成员函数中寻找操作符重载函数
4.语法:
class Type
{
public:
Type operator sign(const Type& p)
{
Type ret;
return ret;
}
};
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> class Complex { int a; int b; public: Complex(int a = 0, int b = 0) { this->a = a; this->b = b; } int getA() { return a; } int getB() { return b; } Complex operator + (const Complex& p) { Complex ret; printf("Complex operator + (const Complex& p)\n"); ret.a = this->a + p.a; ret.b = this->b + p.b; return ret; } friend Complex operator + (const Complex& p1, const Complex& p2); }; Complex operator + (const Complex& p1, const Complex& p2) { Complex ret; printf("Complex operator + (const Complex& p1, const Complex& p2)\n"); ret.a = p1.a + p2.a; ret.b = p1.b + p2.b; return ret; } int main() { Complex c1(1, 2); Complex c2(3, 4); Complex c3 = c1 + c2; // c1.operator + (c2) printf("c3.a = %d, c3.b = %d\n", c3.getA(), c3.getB()); return 0; }
C++标准库
1.C++标准库不是C++语言的一部分
2.C++标准库是由类库和函数库组成的集合
3.C++标准库中定义的类和对象都位于std命名空间中
4.C++标准库的头文件都不带.h后缀
5.C++标准库覆盖了C库的功能
C++编译环境的组成
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> using namespace std; int main() { printf("Hello world!\n"); char* p = (char*)malloc(16); strcpy(p, "D.T.Software"); double a = 3; double b = 4; double c = sqrt(a * a + b * b); printf("c = %f\n", c); free(p); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cmath> using namespace std; int main() { cout << "Hello world!" << endl; double a = 0; double b = 0; cout << "Input a: "; cin >> a; cout << "Input b: "; cin >> b; double c = sqrt(a * a + b * b); cout << "c = " << c << endl; return 0; }
数组操作符的重载
string类最大限度的考虑了C字符串的兼容性
可以按照使用C字符串的方式使用string对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; int main() { string s = "a1b2c3d4e"; int n = 0; for(int i = 0; i<s.length(); i++) { if( isdigit(s[i]) ) { n++; } } cout << n << endl; return 0; }
数组访问操作符([])
1.只能通过类的成员函数重载
2.重载函数能且仅能使用一个参数
3.可以定义不同参数的多个重载函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; class Test { int a[5]; public: int& operator [] (int i) { return a[i]; } int& operator [] (const string& s) { if( s == "1st" ) { return a[0]; } else if( s == "2nd" ) { return a[1]; } else if( s == "3rd" ) { return a[2]; } else if( s == "4th" ) { return a[3]; } else if( s == "5th" ) { return a[4]; } return a[0]; } int length() { return 5; } }; int main() { Test t; for(int i=0; i<t.length(); i++) { t[i] = i; } for(int i=0; i<t.length(); i++) { cout << t[i] << endl; } cout << t["5th"] << endl; cout << t["4th"] << endl; cout << t["3rd"] << endl; cout << t["2nd"] << endl; cout << t["1st"] << endl; return 0; }
重载赋值操作符
1.重载赋值操作符的意义:在需要进行深拷贝的时候必须重载赋值操作符
2.赋值操作符合深拷贝函数有同等重要的意义
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; class Test { int* m_pointer; public: Test() { m_pointer = NULL; } Test(int i) { m_pointer = new int(i); } Test(const Test& obj) { m_pointer = new int(*obj.m_pointer); } Test& operator = (const Test& obj)//注意1.函数的返回值必须是引用2.参数中必须有const修饰 { if( this != &obj )//防止自己赋值给自己 { delete m_pointer; m_pointer = new int(*obj.m_pointer); } return *this; } void print() { cout << "m_pointer = " << hex << m_pointer << endl; } ~Test() { delete m_pointer; } }; int main() { Test t1 = 1; Test t2; t2 = t1; t1.print(); t2.print(); return 0; }
注意:不要混合使用c和c++方式(即C++开发时尽量避免C语言中的惯性思想)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; int main() { const char* p = "12345"; string s = ""; s.reserve(10); // 不要使用 C 语言中的方式操作 C++ 中的字符串 for(int i=0; i<5; i++) { s[i] = p[i]; } cout << s << endl; return 0; }
智能指针
需求
1.需要一个特殊的指针
2.指针生命周期结束时主动释放堆空间
3.一片堆空间最多只能由一个指针标识
4.杜绝指针运算和指针比较
解决方案
1.重载指针特征操作符(->和*)
2.只能通过类的成员函数重载
3.重载函数不能使用参数
4.只能定义一个重载函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; class Test { int i; public: Test(int i) { cout << "Test(int i)" << endl; this->i = i; } int value() { return i; } ~Test() { cout << "~Test()" << endl; } }; class Pointer { Test* mp; public: Pointer(Test* p = NULL) { mp = p; } Pointer(const Pointer& obj)//杜绝多个指针指向同一个内存空间 { mp = obj.mp; const_cast<Pointer&>(obj).mp = NULL; } Pointer& operator = (const Pointer& obj) { if( this != &obj ) { delete mp; mp = obj.mp; const_cast<Pointer&>(obj).mp = NULL; } return *this; } Test* operator -> () { return mp; } Test& operator * () { return *mp; } bool isNull() { return (mp == NULL); } ~Pointer() { delete mp; } }; int main() { Pointer p1 = new Test(0); cout << p1->value() << endl; Pointer p2 = p1; cout << p1.isNull() << endl; cout << p2->value() << endl; return 0; }
继承的概念和意义
组合关系:整体与部分的关系
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; class Memory { public: Memory() { cout << "Memory()" << endl; } ~Memory() { cout << "~Memory()" << endl; } }; class Disk { public: Disk() { cout << "Disk()" << endl; } ~Disk() { cout << "~Disk()" << endl; } }; class CPU { public: CPU() { cout << "CPU()" << endl; } ~CPU() { cout << "~CPU()" << endl; } }; class MainBoard { public: MainBoard() { cout << "MainBoard()" << endl; } ~MainBoard() { cout << "~MainBoard()" << endl; } }; class Computer { Memory mMem; Disk mDisk; CPU mCPU; MainBoard mMainBoard; public: Computer() { cout << "Computer()" << endl; } void power() { cout << "power()" << endl; } void reset() { cout << "reset()" << endl; } ~Computer() { cout << "~Computer()" << endl; } }; int main() { Computer c; return 0; }
结果:
继承关系:父子关系
性能:
- 子类拥有父类的所有的行为和属性
- 子类就是一种特殊的父类
- 子类对象可以当作父类对象使用
- 子类中可以添加父类没有的方法和属性
使用:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; class Parent { int mv; public: Parent() { cout << "Parent()" << endl; mv = 100; } void method() { cout << "mv = " << mv << endl; } }; class Child : public Parent { public: void hello() { cout << "I'm Child calss!" << endl; } }; int main() { Child c; c.hello(); c.method(); return 0; }
结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> using namespace std; class Memory { public: Memory() { cout << "Memory()" << endl; } ~Memory() { cout << "~Memory()" << endl; } }; class Disk { public: Disk() { cout << "Disk()" << endl; } ~Disk() { cout << "~Disk()" << endl; } }; class CPU { public: CPU() { cout << "CPU()" << endl; } ~CPU() { cout << "~CPU()" << endl; } }; class MainBoard { public: MainBoard() { cout << "MainBoard()" << endl; } ~MainBoard() { cout << "~MainBoard()" << endl; } }; class Computer { Memory mMem; Disk mDisk; CPU mCPU; MainBoard mMainBoard; public: Computer() { cout << "Computer()" << endl; } void power() { cout << "power()" << endl; } void reset() { cout << "reset()" << endl; } ~Computer() { cout << "~Computer()" << endl; } }; class HPBook : public Computer { string mOS; public: HPBook() { mOS = "Windows 8"; } void install(string os) { mOS = os; } void OS() { cout << mOS << endl; } }; class MacBook : public Computer { public: void OS() { cout << "Mac OS" << endl; } }; int main() { HPBook hp; hp.power(); hp.install("Ubuntu 16.04 LTS"); hp.OS(); cout << endl; MacBook mac; mac.OS(); return 0; }
结果: