C++

C++ Study Week Two

Posted by LudoArt on March 23, 2019

第二周

一、 类和对象的基本概念(2)

1.1 类的成员函数和类的定义分开写

// 矩形类

class CRectangle
{
    public:
    int w, h;
    int Area(); // 成员函数仅在此处声明
        
    int Perimeter();
    void Init(int w_, int h_);
}
int CRectangle::Area(){
        return w * h;
}
int CRectangle::Perimeter(){
        return 2 * (w + h);
}
void CRectangle::Init(int w_, int h_){
        w = w_; h = h_;
};

1.2 类成员的可访问范围

  • private:私有成员,只能在成员函数内访问
  • public:公有成员,可以在任何地方访问
  • protected:保护成员
  • 若没有上述关键字,则缺省地被认为是私有成员

1.3 成员函数的重载及参数缺省

  • 成员函数也可以重载
  • 成员函数也可以带缺省参数
class Location{
    private:
    int x, y;
    public:
    void init(int x = 0, int y = 0);
    void valueX(int val){ x = val; }
    void valueX(){ return x; }
};

二、构造函数

构造函数:

  • 名字和类名相同,可以有参数,不能有返回值
  • 作用是对对象进行初始化,如给成员变量赋初值
  • 如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数(不做任何操作)
  • 对象生成时构造函数自动调用,一旦生成,就再也不能执行构造函数
  • 一个类可以有多个构造函数
class Complex{
    private:
    double real, imag;
    public:
    Complex(double r, double i);
    Complex(double r);
    Complex(Complex c1, Complex c2);
};
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); // OK

Complex * pc = new Complex(2, 4); // OK

Complex c3(c1, c2); // OK

// 构造函数在数组中的使用

class Test{
    public:
    Test(int n){} //(1)
    
    Test(int n, int m){} //(2)
    
    Test(){} //(3)
    
};
Test array1[3] = {1, Test(1,2)}; //三个元素分别用(1),(2),(3)初始化

Test array2[3] = {Test(2,3), Test(1,2), 1}; //三个元素分别用(2),(2),(1)初始化

Test * pArrar[3] = {new Test(4), new Test(1,2)}; //两个元素分别用(1),(2)初始化

三、复制构造函数

3.1 复制构造函数的基本概念

复制构造函数:

  • 只有一个参数,即对同类对象的引用
  • 形如 X::X( X& ) 或 ` X::X( const X & )` ,二者选一
  • 若没有定义复制构造函数,将会自动生成默认的复制构造函数(完成复制功能)
// 系统自动生成默认复制构造函数

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."

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

// 1) 当用一个对象去初始化同类的另一个对象时

Complex c2(c1);
Complex c2 = c1; // 初始化语句,非赋值语句

// 2) 若某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用

class A{
    public:
    A() {};
    A(A & a){
        cout << "Copy Constructor Called.";
    }
}
void Func(A a1){ }
int main(){
    A a2;
    Func(a2); // 调用了类似了A a1 = a2;这样的功能,故将调用复制构造函数,输出"Copy Constructor Called."
    
    return 0;
}

// 3) 如果函数的返回值是类A的对象,则函数返回时,A的复制构造函数将被调用

A Func(){
    A b;
    return b;
}
int main(){
    Func(); // 输出"Copy Constructor Called."
    
    return 0;
}

注:对象间的赋值并不导致复制构造函数被调用

四、类型转换构造函数和析构函数

4.1 类型转换构造函数

类型转换构造函数:

  • 定义转换构造函数的目的是实现类型的自动转换
  • 只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数
  • 当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量)
class Complex{
    public:
    double real, imag;
    Complex(int i) {//类型转换构造函数
        
        cout << "IntConstructor Called." << endl;
        real = i; imag = 0;
    }
    Complex(double r, double i){
        real = r; imag = i;
    }
};
int main()
{
    Complex c1(7, 8);
    Complex c2 = 12;
    c1 = 9; // 将9自动转换成一个临时的Complex对象
    
    cout << c1.real << ", " << c1.imag << endl; // 输出 9, 0
    
}

4.2 析构函数

析构函数:

  • 名字与类名相同,在前面加 “~” ,没有参数和返回值,一个类最多只能有一个析构函数
  • 析构函数在对象消亡时自动被调用
  • 如果定义类时没写析构函数,则编译器生成缺省析构函数,缺省析构函数什么都不做
class Srting{
    private:
    char * p;
    public:
    String(){
        p = new char[10];
    }
    //析构函数
    
    ~ String(){
        delete []p;
    }
}
int main(){
    String *s = new String[3]; //构造函数调用3次
    
    delete [] s; //析构函数调用3次
    
}

五、构造函数析构函数调用时机

class Demo{
    int id;
    public:
    Demo(int i){
        id = i;
        cout << "id = " << id << " constructed." << endl;
    }
    ~Demo(){
        cout << "id = " << id << " destructed." << endl;
    }
}

Demo ld(1);
void Func(){
    static Demo d2(2);
    Demo d3(3);
    cout << "func" << endl;
}
int main(){
    Demo d4(4);
    d4 = 6;
    cout << "main" << endl;
    { Demo d5(5); }
    Func();
    cout << "main ends" << endl;
    return 0;
}

// 输出结果:

// id = 1 constructed.

// id = 4 constructed.

// id = 6 constructed.

// id = 6 destructed.

// main

// id = 5 constructed.

// id = 5 destructed.

// id = 2 constructed.

// id = 3 constructed.

// func

// id = 3 destructed.

// main ends

// id = 6 destructed.

// id = 2 destructed.

// id = 1 destructed.

六、测验

6.1 奇怪的类复制

class Sample {
public:
	int v;
	Sample() {
		v = 0;
	}
	Sample(int i) {
		v = i;
	}
	Sample(const Sample & x) {
		v = x.v + 2;
	}
};
void PrintAndDouble(Sample o)
{
	cout << o.v;
	cout << endl;
}
int main()
{
	Sample a(5); //调用Sample(int i)
    
	Sample b = a; //调用Sample(const Sample & x)
    
	PrintAndDouble(b); //传入参数需进行复制,故调用Sample(const Sample & x),输出9
    
	Sample c = 20; //调用Sample(int i)
    
	PrintAndDouble(c); //传入参数需进行复制,故调用Sample(const Sample & x),输出22
    
	Sample d; //调用Sample()
    
	d = a; //不调用复制构造函数,故d.v = a.v
    
	cout << d.v; //输出5
    
	return 0;
}

//输出结果:

//9

//22

//5