网站建设计无形资产,织梦网站后台地址,安卓手机app开发软件下载,昆明网站外包C面向对象程序设计-北京大学-郭炜【课程笔记#xff08;三#xff09;】 1、构造函数#xff08;constructor#xff09;1.1、基本概念 2、赋值构造函数2.1、基本概念2.1、复制构造函数起作用的三种情况2.2、常引用参数的使用 3、类型转换构造函数3.1、什么事类型转换构造函… C面向对象程序设计-北京大学-郭炜【课程笔记三】 1、构造函数constructor1.1、基本概念 2、赋值构造函数2.1、基本概念2.1、复制构造函数起作用的三种情况2.2、常引用参数的使用 3、类型转换构造函数3.1、什么事类型转换构造函数 4、析构函数4.1、什么是析构函数4.2、析构函数和数组4.3、析构函数和运算符 delete 5、构造函数析构函数调用时机 开始课程P7 2_2. 构造函数 课程链接程序设计与算法三C面向对象程序设计 北京大学 郭炜 课程PPTgithub提供的对应课程PPT
1、构造函数constructor
1.1、基本概念
1、成员函数的一种 名字与类名相同可以有参数不能有返回值void 也不行作用是对对象进行初始化如给成员变量赋初值如果定义类时没写构造函数则编译器生成一个默认的无参数的构造函数 默认构造函数无参数不做任何操作 如果定义了构造韩素则编译器不生成默认的无参数的构造函数对象生成时构造函数自动调用。对象一旦生成就再也不能在其上执行构造函数一个类可以有多个构造函数 2、为什么需要构造函数 构造函数执行必要的初始化工作有了构造函数就不必再写初始化函数也不用担心忘记调用初始化函数。有时对象没被初始化就使用会导致程序出错。 例1:
// 类中没有写构造函数
class Complex{private:double real, imag;public:void Set(double r, double i);
}; // 编译器自动生成默认构造函数Complex c1 // 默认构造函数被调用
Complex * pc new Complex // 默认构造函数被调用例2:
class Complex{private:double real, imag;pubilc:Complex(double r, double i 0); // 构造函数
}
Complex::Complex(double r, double i){real r; imag i;
}Complex c1; //error,缺少构造函数的参数
Complex * pc new Complex // error没有参数
Complex c1(2); // OK
Complex c1(2,4), c2(3,5);
Complex * pc new Complex(3,4);例3:可以有多个构造函数参数个数或类型不同
class Complex{private:double real, imag;pubilc:// 函数重载Complex(double r, double i 0); // 构造函数Complex(double r, double i);Complex(double r);Complex(Complex c1, Complex c2);
}
Complex::Complex(double r){real r; imag 0;
}
Complex::Complex(double r, double i){real r; imag i;
}
Complex::Complex(Complex c1, Complex c2){real c1.real c2.real;imag c1.imag c2.imag;
}// 构造函数初始化
Complex c1(3), c2(1,0), c3(c1,c2);
// c1 {3, 0}, c2 {1, 0}, c3 {4, 0};例4-1:构造函数在数组中的使用
#includeiostreamclass CSample
{int x;public:CSample(){std::cout Constructor 1 Called std::endl;}CSample(int n){x n;std::cout x x std::endl;std::cout Constructor 2 Called std::endl;std::cout std::endl;}
};int main()
{CSample array1[2]; // 无参数构造函数会被调用两次std::cout step1 std::endl;CSample array2[2] {4, 5};std::cout step2 std::endl;CSample array3[2] {3}; // array3[0]:用的是有参构造函数初始化array3[1]:用的是无参构造函数初始化std::cout step3 std::endl;CSample * array4 new CSample[2];delete []array4;return 0;
}
// OUT
Constructor 1 Called
Constructor 1 Called
step1
x 4
Constructor 2 Called
x 5
Constructor 2 Called
step2
x 3
Constructor 2 Called
Constructor 1 Called
step3
Constructor 1 Called
Constructor 1 Called
zhangbushizhangbushideair beida_lesson % g 04.cpp -o 04
zhangbushizhangbushideair beida_lesson % ./04
Constructor 1 Called
Constructor 1 Called
step1
x 4
Constructor 2 Calledx 5
Constructor 2 Calledstep2
x 3
Constructor 2 CalledConstructor 1 Called
step3
Constructor 1 Called
Constructor 1 Called例4-2:构造函数在数组中的使用
class Test
{public:Test(int n) {} //(1)Test(int n, int m) {} //(2)Test() {} //(3)
};Test array1[3] {1, Test(1,2)};
// 三个元素分别123初始化Test array2[3] {Test(2,3), Test(1,2), 1};
// 三个元素分别用221初始化Test * pArray[3] {new Test(4), new Test(1,2)}; // new的返回值是指针类型
//两个元素分别用12初始化2、赋值构造函数
2.1、基本概念 只有一个参数,即对同类对象的引用。 形如 X::X( X )或X::X(const X ) 二者选一后者能以常量对象作为参数 如果没有定义复制构造函数那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能。 注意事项无参构造函数不一定存在但赋值构造函数一定存在 例1:
class Complex
{private:double real, imag;
};
Complex c1; //调用缺省无参构造函数
Complex c2(c1);//调用缺省的复制构造函数,将 c2 初始化成和c1一样如果定义的自己的复制构造函数则默认的复制构造函数不存在。 class Complex {public :double real,imag;Complex(){ }Complex( const Complex c ) {real c.real;imag c.imag;cout “Copy Constructor called”;}
};
Complex c1;
Complex c2(c1);//调用自己定义的复制构造函数输出 Copy Constructor called不允许有形如 X::X( X )的构造函数。必须要加上引用 class CSample {CSample( CSample c ) {} //错不允许这样的构造函数
};2.1、复制构造函数起作用的三种情况
1、当用一个对象去初始化同类的另一个对象时。
Complex c2(c1);
Complex c2 c1; //初始化语句非赋值语句2、如果某函数有一个参数是类 A 的对象那么该函数被调用时类A的复制构造函数将被调用。
class A
{public:A() { };A( A a) { cout Copy constructor called endl;}
};void Func(A a1){ }
int main(){A a2; // 通过无参构造函数初始化Func(a2); // 调用复制构造函数复制构造函数形参是实参的拷贝不一定return 0;
}
// 程序输出结果为: Copy constructor called3、如果函数的返回值是类A的对象时则函数返回时A的复制构造函数被调用:
# include iostream
class A
{public:int v;A(int n) { v n; };A( const A a) { v a.v;std::cout Copy constructor called std::endl;}
};A Func()
{ A b(4); // 调用A(int n) { v n; }; v 4return b;
}
int main()
{ std::cout Func().v std::endl; return 0;
}// 输出结果
Copy constructor called
44、注意对象之间复制并不导致复制构造函数被调用
#includeiostreamclass CMyclass
{public:int n;CMyclass() {};CMyclass( CMyclass c) { n 2 * c.n ;}
};int main()
{CMyclass c1, c2;c1.n 5; c2 c1; // 对象间赋值CMyclass c3(c1); // 调用复制构造函数std::cout c2.n c2.n ,;std::cout c3.n c3.n std::endl;return 0;
}
// 输出
c2.n 5,c3.n 102.2、常引用参数的使用 void fun(CMyclass obj_). {cout “fun” endl; } 这样的函数调用时生成形参会引发复制构造函数调用开销比较大。所以考虑使用CMyclass 引用类型作为参数如果希望确保实参的值在函数中不应该被改变那么可以加上const关键字 3、类型转换构造函数
3.1、什么事类型转换构造函数 定义转换构造函数的目的是实现类型的自动转换。只有一个参数而且不是复制构造函数的构造函数一般就可以看作是转换构造函数。当需要的时候编译系统会自动调用转换构造函数建立一个无名的临时对象或临时变量。 实例
#includeiostreamclass Complex
{public:double real, imag;Complex( int i ) // 1{std::cout IntConstructor called std::endl;real i; imag 0;}Complex(double r, double i) {real r; imag i;} //2
};int main ()
{Complex c1(7, 8);Complex c2 12;c1 9; // 解释如下/*c1 9; 解释如下1、首先9会被自动转化成一个临时Complex对象即Complex Linshi 92、c1 linshi*/std::cout c1.real , c1.imag std::endl;return 0;
}4、析构函数
4.1、什么是析构函数 实例
class String{private :char * p;public:String () {p new char[10]; //动态分配的内存空间需要释放在析构函数中释放。}
~ String ();
};
String ::~ String() {
delete [] p;
}4.2、析构函数和数组 对象数组生命结束时对象数组的每个元素的析构函数都会被调用。 #includeiostreamclass Ctest
{public:~Ctest() {std::cout destructor called std::endl;}
};int main ()
{Ctest array[2];std::cout End Main std::endl;return 0;
}
// OUT
End Main
destructor called
destructor called4.3、析构函数和运算符 delete delete 运算导致析构函数调用 若new一个对象数组那么用delete释放时应该写 []。否则只delete一个对象调用一次析构函数 Ctest * pTest;
pTest new Ctest; //构造函数调用
delete pTest; //析构函数调用
------------------------------------------------------------------
pTest new Ctest[3]; //构造函数调用3次
delete [] pTest; //析构函数调用3次析构函数在对象作为函数返回值返回后被调用 /*
日期2024.02.17
作者源仔
*/#includeiostreamclass CMyclass
{public:~CMyclass() {std::cout destructor std::endl;}
};CMyclass obj; // 全局对象
CMyclass fun(CMyclass sobj)
{return sobj;/*1、参数对象消亡也会导致析构函数被调用。2、函数调用返回时生成临时对象返回*/
}int main()
{obj fun(obj); // 函数调用的返回值临时对象被return 0; // 用过后该临时对象析构函数被调用
}// OUT
destructor //指的是CMyclass fun(CMyclass sobj)中的CMyclass sobj形参使用结束调用析构函数
destructor //指的是fun(obj)临时变量使用结束调用析构函数
destructor //指的是CMyclass obj;全局对象消完调用析构函数5、构造函数析构函数调用时机
#includeiostream
class Demo
{int id;public:Demo(int i){id i;std::cout id id constructor std::endl;}~Demo(){std::cout id destructed std::endl;}
};Demo d1(1); // 1、全局对象在main函数之前就初始化了就会引发构造函数输出id 1 constructor
void Func()
{static Demo d2(2); // 静态的局部变量整个程序结束静态变量才会消完Demo d3(3);std::cout func std::endl;
}int main()
{Demo d4(4); // 2、输出id 4 constructord4 6; // 3、调用类型转换构造函数构建为6的临时构造函数输出id 6 constructor临时构造函数调用完就会直接销毁引发析构函数调用输出id destructedstd::cout main std::endl; // 输出main{Demo d5(5); // 4、局部对象输出id 5 constructor} // 5、局部变量销毁引发析构函数调用。输出id destructedFunc(); // 6、如下/*6、输出id 2 constructor7、输出id 3 constructor8、输出Func9、静态的局部变量整个程序结束静态变量才会消完所以不会先引发 static Demo d2(2)的析构函数10、先引发Demo d3(3);的析构函数输出id destructed*/std::cout main ends std::endl; // 11、输出main ends/*12、引发d4 6;中d4的析构函数调用注意之前引发的析构函数是 6 创建临时构造函数引发的析构函数调用输出id destructed13、引发static Demo d2(2);的析构函数调用输出id destructed14、引发Demo d4(4);的析构函数调用输出id destructed*/return 0;
}/*
id 1 constructor
id 4 constructor
id 6 constructor
id destructed
main
id 5 constructor
id destructed
id 2 constructor
id 3 constructor
func
id destructed
main ends
id destructed
id destructed
id destructed
*/实例5: 假设A是一个类的名字下面的程序片段会类A的调用析构函数几次 答案调用3次。 解释new创建的动态变量必须要释放才能引发析构函数的调用。 int main()
{A * p new A[2];A * p2 new A;A a;delete [] p;
}