1,对象的构造通过构造函数来完成,和类名相同且没有返回值,这个时候只有参 数一个特性,构造函数可以自定义参数,这个参数一般而言就是对类进行初始 化来使用的;带有参数的构造函数的意义在于可以使得每个对象有不同的初始 化状态(生活中每个事物必然包含自己的初始化状态,不如人的出生,面向对 象用来将生活中的事物映射的程序设计领域,所以现实世界的情况都必须可以 用面向对象的语言来描述,因此带有参数的构造函数就是非常必要的);
2,带有参数的构造函数:
1,构造函数可以根据需要定义参数;
2,一个类中可以存在多个重载的构造函数;
3,构造函数的重载遵循 C++ 重载的规则;
4,代码示例:
1 class Test 2 { 3 public: 4 Test(int v) 5 { 6 // use v to initialize member 7 } 8 };
3,对象定义和对象声明的区别:
1,对象定义:申请对象的空间并调用构造函数;
1,第一步,必须申请对象所占用的内存空间;
2,第二步,调用构造函数;
2,对象声明:告诉编译器存在这样一个对象;
1,对象在哪里定义的不知道,链接的时候回去找;
2,预处理,编译器对源代码进行检查并生成目标文件,链接器在各个目标文件中寻找目标文件存在的一些名字;
3,对象声明时,没有对象定义时的两个步骤;
3,代码示例:
1 Test t; // 定义对象并调用构造函数; 2 3 Int main() 4 { 5 // 告诉编译器存在名为 t 的Test对象; 6 extern Test t; 7 8 return 0; 9 }
4,对象的声明中可以在构造函数参数中给出默认值,对象的定义中不能够在构 造函数参数中给出默认值;
4,构造函数的自动调用(第一种初始化对象方式):
1 #include <stdio.h> 2 3 class Test 4 { 5 public: 6 Test() 7 { 8 printf("Test() "); 9 } 10 11 Test(int v) 12 { 13 printf("Test(int v), v = %d ", v); 14 } 15 }; 16 17 int main() 18 { 19 Test t; // 调用 Test() 20 Test t1(1); // 初始胡第一种方式的参数式自动调用,调用 Test(int v);这里也是定义对象,看上去非常像函数调用,但是这里是告诉编译器要调用带有参数的函数,由重载规则确定调用的是那个构造函数; 21 Test t2 = 2; // 初始化第一种方式的赋值式自动调用,调用 Test(int v);C 语言中初始化的方法,定义一个变量后,立即指明一个值,通过赋值符号指明;这在面向对象中其实也是对象的定义,并且指明想用右值初始化左值; 22 23 t = t2; // 这是赋值操作,这里运行后不会调用构造函数,没有打印语句;初始化会调用构造函数,赋值则看后续课程; 24 25 int i = 1; // 用 1 对 i 进行初始化; 26 i = 1; // 用 1 对 i 进行赋值;赋值和初始化是不同的;在面向对象当中,不同在于初始化是要调用构造函数的; 27 28 int i(100); // 初始化的第二种写法,同 int i = 100;; 29 30 printf("i = %d ", i); 31 32 return 0; 33 }
1,实验结果说明:
1,初始化和赋值看上去相同之处在于当初始化用赋值符号表达的时候;
2,不同之处在于初始化要调用构造函数,而赋值不会;
5,构造函数的调用:
1,一般情况下,构造函数在对象定义时被自动调用;
2,一些特殊情况下,需要手工调用构造函数:
1,如何创建对象数组;
6,构造函数的手工调用(第二种初始化对象方式)编程实验:
1 #include <stdio.h> 2 3 class Test 4 { 5 private: 6 int m_value; 7 public: 8 Test() 9 { 10 printf("Test() "); 11 12 m_value = 0; 13 } 14 Test(int v) 15 { 16 printf("Test(int v), v = %d ", v); 17 18 m_value = v; 19 } 20 int getValue() 21 { 22 return m_value; 23 } 24 }; 25 26 int main() 27 { 28 Test ta[3]; // 按照 C 语言的方法定义 3 个 Test 对象的数组 ta;结果调用了 3 个 Test() 函数; 29 30 for(int i=0; i<3; i++) // 循环结果打印出 3 个 0,这不一定是我们想要的;编译器默认的调用了 Test(); 31 { 32 printf("ta[%d].getValue() = %d ", i , ta[i].getValue()); 33 } 34 35 Test ta[3] = {Test(), Test(1), Test(2)}; // 手动调用构造函数; 36 37 for(int i=0; i<3; i++) // 循环结果为 0 1 2;分别调用了相应的构造函数; 38 { 39 printf("ta[%d].getValue() = %d ", i , ta[i].getValue()); 40 } 41 42 Test t = Test(100); // 初始化第二种方式,手工调用构造函数; 43 44 printf("t.getValue() = %d ", t.getValue()); 45 46 return 0; 47 }
7,小实例:
1,需求:开发一个数组类解决原生数组的安全性问题:
1,提供函数获取数组长度;
1,C++ 要兼容 C 语言中的数组,但是 C 语言中的数组没有长度信息,用着用着就越界了;
2,提供函数获取数组元素;
3,提供函数设置数组元素;
8,数组类的实现编程实验:
1,IntArray.h 文件:
1 #ifndef _INTARRAY_H_ 2 #define _INTARRAY_H_ 3 4 class IntArray 5 { 6 private: 7 int m_length; 8 int* m_pointer; 9 public: 10 IntArray(int len); 11 int length(); 12 bool get(int index, int& value); // 用 bool 类型作为返回值是为了安全性,安全性的体现见数组类的实现; 13 bool set(int index ,int value); 14 void free(); // 用来释放 m_pointer 指向的堆空间 15 }; 16 17 #endif
2,IntArray.cpp 文件:
1 #include "IntArray.h" 2 3 IntArray::IntArray(int len) // 不加作用域分辨符时,是全局函数(此时仅名字前缀相同而已),所以要加作用域分辨符指明是数组类中的函数; 4 { 5 m_pointer = new int[len]; // new int(len) 指的是设置初始值; 6 7 for(int i=0; i<len; i++) 8 { 9 m_pointer[i] = 0; 10 } 11 12 m_length = len; 13 } 14 15 int IntArray::length() 16 { 17 return m_length; 18 } 19 20 bool IntArray::get(int index, int& value) 21 { 22 bool ret = (0 <= index) && (index < length()); // 进行安全性检查,是安全性的体现; 23 24 if( ret ) 25 { 26 value = m_pointer[index]; // 通过引用返回一个值; 27 } 28 29 return ret; // 越界则返回 false; 30 } 31 32 bool IntArray::set(int index, int value) 33 { 34 bool ret = (0 <= index) && (index < length()); 35 36 if( ret ) 37 { 38 m_pointer[index] = value; 39 } 40 41 return ret; 42 } 43 44 void IntArray::free() 45 { 46 delete[]m_pointer; 47 }
3,IntArray 的使用:
1 #include <stdio.h> 2 #include "IntArray.h" 3 4 int main() 5 { 6 IntArray a(5); 7 8 for(int i=0; i<a.length(); i++) 9 { 10 a.set(i, i + 1); 11 } 12 13 for(int i=0; i<a.length(); i++) 14 { 15 int value = 0; 16 17 if( a.get(i, value) ) 18 { 19 printf("a[%d] = %d ", i, value); 20 } 21 } 22 23 a.free(); 24 25 return 0; 26 }
4,这里展示了面向对象的强大,我们可以通过类来封装一些之前学习到的概念,并且可以将这些概念上的缺陷通过封装来弥补开来;
9,小结:
1,构造函数可以根据需要定义参数;
2,构造函数之间可以存在重载关系;
3,构造函数遵循 C++ 中重载函数的规则;
4,对象定义时会触发构造函数的调用;
1,构造函数调用方式分为自动调用和手工调用两种;
2,自动调用的形式又分为参数式和赋值式;
3,即初始化时,分为两种调用方式三种书写形式;
5,在一些情况下可以手动调用构造函数;