C++ 大学MOOC 北大课程(郭炜老师)听课整理 第二周(构造函数)

类可访问范围

访问范围说明符

1)private:用来指定私有成员
2)public:用来指定公有成员
3)protected:用来指定保护成员
4)成员访问范围由离它前面最近的说明符决定
5)如果某个成员前没有访问范围说明符,则对于class来言该成员默认为私有成员,对于struct而言该成员默认为公有成员

隐藏机制

1)类的成员函数内部可以访问:当前对象的全部属性、函数;同类其他对象的全部属性、函数
2)在类的成员函数外部:该类对象的公有成员
3)对私有成员的访问只能通过成员函数来访问

成员函数重载、参数缺省

1)使用参数缺省时要注意由于函数重载所引起的二义性
例如:

class location{
private:
 int x, y;
public:
 void init(int x=0,int y=0)
 void value(int val = 0){ x = val; }//此处成员函数参数缺省
 int value(){ return x; }
};
location a;
a.value();//此处编译出错,无法判断应该调用哪一个value()函数

构造函数

构造函数的作用是在对象生成时对其进行初始化,构造函数有很多种(普通构造函数、类型转化构造函数、复制构造函数),构造函数也可以定义多个(参数个数或类型不同),对象生成时调用哪个构造函数需要分情况讨论。

基本概念

1)构造函数时成员函数的一种,函数名就是类名,没有返回值
2)如果定义类时没有写构造函数,则编译器自动生成一个不做任何操作的无参构造函数 如果写了构造函数,则不生成默认无参构造函数
3)构造函数在对象生成时自动调用,对象一旦生成,则再也不会调用构造函数
例如:

class Complex{
private:
 double real, imag;
public:
 Complex(double r, double i);//构造函数(1)
 Complex(double r);//构造函数(2)
 Complex(Complex c1, Complex c2);//构造函数(3)
};
Complex::Complex(double r, double i){
 real = r; imag = i;
}
Complex::Complex(double r){
 real = r; imag = 0;
}
Complex::Complex(Complex c1, Complex c2){
 real = c1.real + c2.real;
 imag = c1.imag + c2.imag;
}
Complex c1;//编译出错,因为没有提供构造函数的参数
Complex * p = new Complex;//编译出错
Complex c2(3);//使用构造函数(2) 
Complex c3(1,0);//(1)
Complex c4(c2,c3);//(3)

构造函数在数组中的使用

1)给出对象数组各个元素的初始化参数,编译器根据初始化参数调用相应的构造函数
例如:

class Complex{
private:
 double real, imag;
public:
 Complex(){}//(1)
 Complex(int n){}//(2)
 Complex(int n,int m){}//(3)
};
Complex arrey1[2];//两个对象元素都用(1)来初始化
Complex * arrey2= new Complex[2];//动态分配的每一个对象都用(1)来初始化
Complex arrey3[2] = {4,5};//两个对象数组都用(2)来初始化
Complex arrey4[2] = {3};//数组中第一个对象用(2)来初始化,第二个元素用(1)初始化
Complex arrey5[3] = { 2, Complex (4,6)};//第一个对象用(2)来初始化,第二个对象用(3)来初始化,第三个对象用(1)来初始化
Complex arrey6[3] = { Complex(2, 3), Complex(1, 2), 1 };//前两个对象都用(3)来初始化,第三个对象用(2)来初始化
Complex *arrey[3] = { new Complex(1, 2), new Complex (4)};//数组元素是指针,第一个指针指向一个动态分配的类Complex对象空间,该空间用(3)来初始化,第二的指针指向的类Complex对象空间用(2)来初始化,第三个指针元素没有初始化其指向某个特定的空间,故没有对象生成,也就没有调用构造函数

复制构造函数

基本概念

1)只有一个参数,即对同类对象的引用
2)方式:X::X(X& )或X::X(const X& ) 后者参数为常引用 故能以常量对象作为参数
3)如果没有自定义复制构造函数,则编译器生成默认复制构造函数,完成对象复制的工作
4)复制构造函数只有一个 要么自定义 要么默认
例如:

class Complex{
private:
 double real, imag;
public:
 Complex(){}
 Complex(const Complex & r) {
  real = r.real;
  imag = r.imag;
 }
};
Complex c1;//调用无参构造函数初始化
Complex c2(c2);//调用自定义复制构造函数初始化

复制构造函数起作用的三种情况

1)第一用一个对象去初始化同类的另一个对象时
例如:

class Complex{
private:
 double real, imag;
public:
 Complex(){}
 Complex(const Complex & r) {
  real = r.real;
  imag = r.imag;
 }
};
Complex c1;
Complex c2 = c1;//不是赋值语句是初始化语句,调用复制构造函数
Complex c3(c1);//初始化语句,调用复制构造函数

2)对于参数是某类对象的函数,调用该函数时将调用该类的复制构造函数
例如:

class A{
public:
 A(){}
 A(A& r){
  cout << "copy constructor called" << endl;
 }
};
void func(A a1){}//参数是类A对象
int main(){
 A a2;
 func(a2);//调用func函数时将调用类A的复制构造函数
 return 0;
}

输出:

copy constructor called

3)如果函数的返回值时类A的对象时,该函数返回时将调用类A的复制构造函数
例如:

class A{
public:
 int n;
 A(int v){
  n = v;
 }
 A(A& r){
  n = r.n;
  cout << "copy constructor called" << endl;
 }
};
A func(){
 A b(4);
 return b;
}
int main(){
 cout << func().n << endl;
 return 0;
}

调用func函数,当该函数返回时就会调用类A的复制构造函数,函数参数就是 b
输出:

copy constructor called
4

类型转换构造函数

1)作用是实现类型的自动转换
2)只有一个参数且不是复制构造函数的构造函数可以看作是类型转换构造函数
3)当需要时,编译器会自动调用类型转换构造函数,建立一个临时对象
例如:

class complex{
public:
 double real, imag;
 complex(int n){
  cout << "intconstructor called" << endl;
  real = n; imag = 0;
 }
 complex(double r, double i){
  real = r;
  imag = i;
 }
};
int main(){
 complex c1(7, 8);
 complex c2 = 12;//调用构造函数对c2初始化
 c1 = 9;//调用类型转换函数生成临时对象对c1赋值
 cout << c1.real << "," << c1.imag << endl;
 return 0;
}

输出:

intconstructor called
intconstructor called
9,0

4)显示的类型转换构造函数 在前加explicit说明符
5)用显示类型转换构造函数的方式:类名(参数);
例如:

class complex{
public:
 double real, imag;
 explicit complex(int n){
  cout << "intconstructor called" << endl;
  real = n; imag = 0;
 }
 complex(double r, double i){
  real = r;
  imag = i;
 }
};
int main(){
 complex c1(7, 8);
 complex c2 = complex(12);
 c1 = complex(9);
 cout << c1.real << "," << c1.imag << endl;
 return 0;
}

输出:

intconstructor called
intconstructor called
9,0

析构函数

1)名字和类名相同,在前加‘~’,没有参数和返回值,一个类最多只有一个析构函数
2)析构函数在对象消亡时做一些善后工作
3)如果没写析构函数,则编译器自动生成什么也不做的缺省的析构函数
例如:

class complex{
public:
 ~complex(){
  cout << "destructor" << endl;
 }
};
complex obj;
complex fun(complex sobj){//函数返回结束时对象参数会消亡
 return sobj;//函数返回时会生成临时对象
}
int main(){
 obj = fun(obj);//此语句结束时,函数返回时生成的临时对象会消亡
 return 0;//main函数结束时全局对象obj消亡
}

输出:

destructor
destructor
destructor

构造函数与析构函数的调用

例1:

class demo{
 int id;
public:
 demo(int i){
  id = i;
  cout << "id=" << id << "constructed" << endl;
 }
 ~demo(){
  cout << "id=" << id << "destructed" << endl;
 }
};
demo d1(1);//1先生成全局对象 d1 调用构造函数初始化
void func(){
 static demo d2(2);//生成静态对象 d2 调用构造函数对其初始化
 demo d3(3);//生成局部对象 调用构造函数对其初始化
 cout << "func" << endl;
}
int main(){
 demo d4(4);//2生成局部对象 d4 调用构造函数初始化
 d4 = 6;//3调用类型转换构造函数生成临时对象;临时对象赋值给 d4;4赋值语句结束后临时对象消亡
 cout << "main" << endl;
 {
  demo d5(5);//5生成对象 d5 调用构造函数对其初始化
 }//6对象 d5 消亡
 func();//7调用func()函数;8func()函式返回后局部对象 d3 消亡
 cout << "main end" << endl;
 return 0;//9main()函数结束后局部对象 d4 静态对象 d2 全局对象 d1 消亡(先构造的后析构)
}

输出:

id=1constructed
id=4constructed
id=6constructed
id=6destructed
main
id=5constructed
id=5destructed
id=2constructed
id=3constructed
func
id=3destructed
main end
id=6destructed
id=2destructed
id=1destructed

例2:

class demo{
public:
 demo(){
 }
 demo(demo & r){
  cout << "copy constructor" << endl;
 }
 ~demo(){
  cout << "destructed" << endl;
 }
};
void func(demo obj){
 cout << "func" << endl;
}
demo c;
demo test(){
 cout << "test" << endl;
 return c;
}
int main(){
 demo c1;
 func(c1);
 test();
 return 0;
}

输出:

copy constructor//调用func()时调用复制构造函数
func
destructed//func()函数返回结束 参数obj消亡
test
copy constructor//test()函数返回时生成临时对象 调用复制构造函数
destructed//临时对象消亡
destructed//局部对象消亡
destructed//全局对象消亡
发布了16 篇原创文章 · 获赞 17 · 访问量 760

猜你喜欢

转载自blog.csdn.net/weixin_45644911/article/details/104138288