前篇:[C++ 笔记] 类和对象基础

# 代理构造

一个构造函数调用另外一个构造函数:

public:
	A(): A(0){}
	A(int i): A(i, 0){}
	A(int i, int j)
 	{
		num1=i;
		num2=j;
		average=(num1+num2)/2;
	}

一定要避免环形调用,递归调用也是:

// 上段第三个 ctor 改为:
    A(int i, int j): A(){}
// 出现环形调用 omo

踩坑记录:

#include <iostream>
using namespace std;
class C
{
public:
    int x;
    C()
    {
        C(10); 
    }
    C(int i): x{i} {}
};
int main()
{
    C c;
    cout << c.x << endl; // 输出不是 10
    return 0;
}

加一下高亮:<img src="https://s3.ax1x.com/2021/01/27/sznUyD.png"style="zoom:50%;" /> 这个 C(10); 是一个匿名对象 (⊙x⊙;)

在初始化列表处是调用,在 ctor 内是创建匿名对象 (﹁ ﹁) ~→

# 不可变对象

对象创建后,其内容不可改变,除非通过成员拷贝。不可变对象所属的类称不可变类。

必要条件:

  • 所有数据域均为私有属性。
  • 无更改器函数。

ex:满足上述不一定是不可变对象, 若存在成员函数 返回 指向私有成员的指针,则可在对象外部修改数据成员。

# 实例成员与静态成员

用关键字 static 声明不绑定到类实例的成员。

  • C++17: constexpr 必须在类中声明并初始化。 inline const 可以在类中声明并初始化。
  • 大部分情况必须在类外定义并初始化,且不带 static 关键字。

实例:

#include <iostream>
using namespace std;
class Square{
private:
    double side {1.0}; // 实例成员
    static int numberOfObjects; // 静态成员
public:
    double getside() { return side; }
    void setside(double side) { this -> side = side; }
    Square(double side)
    {
        this -> side = side;
        numberOfObjects++;
    }
    Square() : Square(1.0) {}
    static int getNumbers() { return numberOfObjects; }
    int getN() { return numberOfObjects; }
    double area() { return side * side; }
};
int Square::numberOfObjects; // 初始化 0,不写会 CE
int main()
{
    Square s1;
    cout << s1.getNumbers() << endl; // 1
    Square s2{2.0};
    cout << s2.getN() << endl; // 2
    cout << Square::getNumbers() << endl; // 2 使用类名
    cout << Square::getN() << endl; // Err: 非静态成员引用必须与特定对象相对
    return 0;
}

# 析构函数 dtor

  • 类名前加 ~ ,在对象销毁时自动调用。
  • 无参数,无返回值,不可重载。
  • 自动生成析构函数,可使用 defaultdelete (C++11)
#include <iostream>
using namespace std;
class Square{
private:
    static int numberOfObjects;
public:
    Square() { numberOfObjects++; }
    static int getNumbers() { return numberOfObjects; }
    ~Square() { numberOfObjects--; }
};
int Square::numberOfObjects;
int main()
{
    Square s1, s2;
    cout << Square::getNumbers() << endl; // 2
    delete &s2;
    cout << Square::getNumbers() << endl; // 1
    return 0;
}

其它应用:作用域相关,栈区自动销毁时可以进行一些蜜汁操作 qwq

# 友元

授权某些可信的外部函数和类可访问私有成员, 特点是打破了封装性。

使用关键字 friend类的内部声明友元。

实例:

#include <iostream>
using namespace std;
class Square
{
private:
    double side;
public:
    Square(double side = 1.0)
    {
        this -> side = side;
    }
    friend class Calculator; // 友元类
    friend double getSide(const Square& s); // 友元函数
};
class Calculator
{
public:
    double Square_area(const Square& s)
    {
        return s.side * s.side;
    }
};
double getSide(const Square& s)
{
    return s.side;
}
int main()
{
    Square s1{2.0};
    Calculator cal;
    cout << cal.Square_area(s1) << endl;
    cout << getSide(s1) << endl;
    system("pause");
    return 0;
}

# 拷贝构造函数

拷贝构造:用一个对象初始化另一个同类对象。

声明拷贝构造函数: Circle(Circle&);Circle(const Circle&);

  • 首个参数必须是同类型引用, C++11 可带有默认值的其它参数。
  • 仅拷贝构造时会调用拷贝构造函数,区分赋值与拷贝构造,例如 Circle c2 = c1; 拷贝构造。
  • 没有显式声明时,编译器会隐式声明 cp ctor。

实例:

#include <iostream>
using namespace std;
class C
{
private:
    static int num;
public:
    C() { num++; }
    C(C&)
    {
        num++;
        cout << "copyed c" << num << endl;
    }
};
int C::num = 0;
int main()
{
    C c1;
    C c2 = c1;
    C c3;
    c3 = c1;
    C c4{c1};
    return 0;
} // 输出 copyed c2, copyed c4

# 深拷贝与浅拷贝

深浅拷贝仅对于类中的指针成员而言。

# 浅拷贝 shallow copy

数据域是一个指针,拷贝时只拷贝指针地址,而不拷贝指向内容。你老婆真棒(

  • 创建新对象时的隐式 / 默认构造函数。
  • = 为已有对象赋值的默认运算符。

实例:

#include <iostream>
using namespace std;
class C
{
private:
    int *p;
public:
    C(int x = 233)
    {
        p = new int;
        *p = x;
    }
    C(C&) = default;
    void setP(int x) { *p = x; }
    int getP() { return *p; }
};
int main()
{
    C c1, c2{c1};;
    cout << c1.getP() << endl; // 233
    c2.setP(466);
    cout << c1.getP() << endl; // 466
    return 0;
}

# 深拷贝 deep copy

拷贝指针指向的内容。

  • 自行编写拷贝构造函数。
  • 重载赋值运算符。

实例:仅需修改上段 cp ctor

......
C(C& c)
{
    p = new int {*c.p};
}
......
int main()
{
    C c1, c2{c1};
    cout << c1.getP() << endl; // 233
    c2.setP(466);
    cout << c1.getP() << endl; // 233
    cout << c2.getP() << endl; // 466
    return 0;
}
更新于