C++之小试牛刀

C到C++的升级

1.变量的定义

C++中所有的变量都可以在使用的时候再定义

C中的变量都必须在作用域开始的位置定义

2.register关键字

C++为了兼容C,所以也支持register关键字,但是在C++的实际编译过程中已经忽略了register关键字,所以得出了与C中的一点不同:在C中对于register修饰的变量不可以取地址,而在C++中对于使用register修饰的变量却是可以取地址操作的

3.定义多个同名的全局变量

在C语言中,重复定义多个同名的全局变量是合法的,会把所有的连接到同一个地址

在C++中,不允许定义多个同名的全局变量

 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 }
C++中不可定义同名的全局变量和register的验证

4.struct关键字

C语言中的struct定义了一组变量的集合

C语言中的struct定义的标识符并不是一种新的类型

C++中的struct用于定义一种全新的类型

5.标识符

C++中所有的标识符都必须显示的声明类型,在C中的默认类型在C++中是不合法的

 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 }
View Code

const关键字

C++在C的基础上对const进行了优化处理

在C++中碰见const声明时在符号表中放入常量,编译过程中若发现使用常量则直接以符号表中的值替换

编译过程中若发现以下情况则给对应的常量分配存储空间:

1.对const常量使用了extern

2.对const常量使用&操作符

注:C++编译器虽然会在上面的两种情况下为const常量分配空间,但是不会使用其存储空间中的值

 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 }
C++中的const
 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 }
const与宏

布尔类型和引用

C++在C语言基本类型系统的基础上增加了bool(布尔)类型

C++中的bool可取的值只有true(非0)和false(0)这两个值,

C++编译器会把非0值转换为true,0值转换为false

理论上bool只占用一个字节

 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 }
验证bool在程序中只有1和0两个值

三目运算符

C语言中的三目运算符返回的是变量值,故不可以作为左值使用

C++中的三目运算符可直接返回变量本身(返回的是变量的引用),故既可以作为左值又可以作为右值使用

注:三目运算符返回的值中如果有一个是常量值,则与C语言中的用法一样,返回的是变量的值

 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 }
View Code

引用

1.引用可以看成是一个已经定义的变量的别名

2.语法:Type &name = var;

注:普通引用在定义时必须用同类型的变量进行初始化

 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的值
 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 }
引用的特殊意义
 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 }
引用的思考

引用的本质

 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))属性

 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++中可以在函数声明的时候为参数提供一个默认值,当函数调用没有提供参数的值的时候,则使用默认值

注:参数的默认值必须在函数声明中指定,在定义种参数不可以出现默认值

 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.函数调用的时候是从左到右匹配的

 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语言程序中可能出现的不规范写法

 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 }
占位参数

 函数重载

概念:同一个标识符在不同的上下文中有不同的意义

实现:使用同一个函数名定义不同的函数,同一个函数名和不同的参数搭配的时候代表的函数含义是不同的

 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.函数重载必须发生在同一个作用域

注:函数的返回值不可以作为重载的依据,函数重载是由函数名参数列表决定的

 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.严格匹配候选者的函数类型与函数指针的函数类型

 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

 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
View Code

 动态内存分配

1.C++中的内存分配是通过new关键字进行的

2.C++中的动态内存的申请是基于类型进行的

3.delete关键字用于内存的释放

4.格式:

变量的申请

Type *pointer = new Type;

delete pointer;

数组的申请

Type * pointer = new Type[N];

delete[] pointer;

Type:代表类型

N:代表个数

#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;
}
C++中的动态内存分配

new与malloc的区别

new关键字是C++的一部分

malloc是由C库提供的函数

new是以具体类型为单位进行内存分配

malloc是以字节为单位进行内存分配的

new在申请单个类型变量时可进行初始化

malloc不具备内存初始化的特性

#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;

#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)

#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引用的类型与初始化变量的类型

相同:初始化变量成为只读变量

不同:生成一个新的只读变量

#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;
}
const典型问题

运行结果:

引用与指针之间的关系

指针是一个变量

1.值为一个内存地址,不需要初始化,可以保存不同的地址

2.通过指针可以访问对应内存中的值

3.指针可以被const修饰成为常量或者只读变量

引用只是一个变量的新名字

1.对引用的操作(赋值,取值等)都会传递到代表的变量上

2.const引用时代表的变量具有只读属性

3.引用必须在定义时初始化,之后无法代表其他的变量

没有数组的引用

#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;
}
View Code

 类与封装的概念

C++中类的封装

成员变量:C++中用于表示类属性的变量

成员函数:C++中用于表示类行为的函数

C++中可以给成员变量和成员函数定义访问级别

public:成员变量和成员函数可以在类的内部和外部访问和调用

private:成员变量和成员函数只能在类的内部被访问和调用

注:类的封装机制使得使用方式和内部细节相分离

#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.类成员的作用域与访问级别没有关系

#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

#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;
}
class的初探


#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;
}
View Code

 构造函数

1.构造函数名与类名相同

2.构造函数无任何返回类型的声明

3.构造函数在对象定义的时候自动被调用

#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.带参数的构造函数(类似于函数重载,遵循函数重载的原则)

#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)一些特殊情况下,需要手工调用构造函数

#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)拷贝构造函数

.当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,简单的进行成员变量的值的复制

#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.什么时候进行深拷贝

--对象中有成员指代了系统中的资源

#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.初始化列表先于构造函数的函数体执行

#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成员的初始值,因此无法进入符号表成为真正意义上的常量

#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;

对象的构造顺序

局部对象:当程序执行到达对象定义语句时进行构造

#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创建对象将自动触发构造函数的调用

#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;
}
堆对象构造顺序

注:全局对象的构造顺序不确定,这个有编译器决定,不同编译器构造顺序不同

析构函数

#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;
}
构造函数

自己定义析构函数的准则

当类中自定义了构造函数,并且构造函数中使用了系统资源(如:内存申请,文件打开等)则需要自己定义析构函数

 神秘的临时对象

#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;
}
View Code

程序意图:

在Test()中以0作为参数调用Test(int i)

将成员变量mi的初始值设置为0

运行结果:

成员变量mi的值为随机值

原因:

1.直接调用构造函数将产生一个临时对象

2.临时对象的生命周期只有一条语句的时间

3.临时对象的作用域只在一条语句中

4.临时对象是C++中值得警惕的灰色地带

#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++编译器会将少临时对象的产生

#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;
}
View Code

注:c++中临时对象和野指针是bug的主要来源

单个对象创建时构造函数的调用顺序

1.调用父类的构造过程

2.调用成员变量的构造函数(调用顺序与声明的顺序相同)

3.调用类自身的构造函数

对于栈对象和全局对象,类似于入栈与出栈的顺序,最后构造的对象最先被析构

对于堆对象析构的发生于delete的使用顺序有关

析构函数与构造函数的顺序相反

#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用于指代当前对象

#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;
}
成员函数
#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.静态成员变量在程序内部位于全局数据区

#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.可以通过对象名访问公有静态成员函数

半成品对象

初始化操作不能按照预期完成而得到的对象

#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.需要使用资源的操作

可能出现异常情况,如:内存申请,访问文件

#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.友元不是类的一部分

#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.类的友元可以是某个完整的类

#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;
}
View Code

类中函数的重载

#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.扩展系统中已经存在的函数功能

#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;

}

};

#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++编译环境的组成

#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;
}
C++标准库中的C库兼容

#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;
}
C++中的输入和输出

数组操作符的重载

string类最大限度的考虑了C字符串的兼容性

可以按照使用C字符串的方式使用string对象

#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;
}
用C方式使用string类

数组访问操作符([])

1.只能通过类的成员函数重载

2.重载函数能且仅能使用一个参数

3.可以定义不同参数的多个重载函数

#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.赋值操作符合深拷贝函数有同等重要的意义

#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语言中的惯性思想)

#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;
}
View Code

 智能指针

需求

1.需要一个特殊的指针

2.指针生命周期结束时主动释放堆空间

3.一片堆空间最多只能由一个指针标识

4.杜绝指针运算和指针比较

 解决方案

1.重载指针特征操作符(->和*)

2.只能通过类的成员函数重载

3.重载函数不能使用参数

4.只能定义一个重载函数

#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;
}
View Code

继承的概念和意义

组合关系:整体与部分的关系

 

 

#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;
}
View Code

结果:

继承关系:父子关系

性能:

  1. 子类拥有父类的所有的行为和属性
  2. 子类就是一种特殊的父类
  3. 子类对象可以当作父类对象使用
  4. 子类中可以添加父类没有的方法和属性

使用:

 

#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;
}
View Code

结果:

#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;
}
View Code

结果:

 

posted @ 2020-09-10 17:59  夜空的北极星  阅读(187)  评论(0编辑  收藏  举报