cpp拾遗——构造, new, delete, 静态成员


1. 构造

构造函数的调用

T t(1,2);     // 调用 T(int ,int)
T t2 = (1,2); // 调用 T(int)
T t3 = 1;     // 调用 T(int)
T t3 = T(1);  // 调用 T(int) 构造匿名对象,并将 匿名对象 和符号 t3绑定
T t4 = t3;    // 调用 T(T&)
T t5(t4);     // 调用 T(T&)

void func(T t);
func(t5);     // 调用 T(T&) 构造形参t

class T {
public:
   T(int a); // 当定义了构造函数,编译器就不提供构造函数
}
T a; // 所以这里错误

1.1 构造函数初始化列表

当使用组合对象时,可能需要使用初始化列表

class A{
public:
   A(int a);
}
class B {
public:
   B(int a, int b);
   A m_a;
}

B(1,2); // 这里会报错,由于构造B 时,会先构造A,但是无法调用A的有参构造。
// 所以必须改成
class B{
public:
  B(int a, int b) : m_a2(a), m_a(b) {}
  A m_a;
  A m_a2;
}

组合对象的构造顺序和定义顺序有关,和初始化列表顺序无关。
析构顺序和构造顺序相反

若类有const属性,则必须用参数化列表进行初始化。

class A{
public:
   A(int a) :m_a(a) {}
private:
   const int m_a;
};

2. 匿名对象

函数返回对象时,会构造一个匿名对象,匿名对象是否被析构,取决于是否又符号去绑定。

T f() {
   T a;
   return a;  // 调用 T(T&) 构造匿名对象
}

T t1 = f(); // 匿名对象又符号接,所以不会被析构
T t2;
t2 = f();  // 匿名对象没符号接,会被析构

3. new delete

3.1 基本用法

int *a = new int;
int *arr = new int[5];
delete a;
delete []arr;

3.2 new delete 和 malloc free

new delete 调用时意味构造析构调用,
对于基础类型,new delete 和 Malloc free 相同,所以可以交叉调用,
对于自定义类型,不能交叉调用。

4. 静态成员

4.1 基本使用

class T{
    public:
        // 函数或属性 使用 static 声明,则为静态类型
        static int g_num;
        // 静态成员函数内只能使用 静态成员属性,不能使用非静态成员属性
        static int get_num()
        {
            return g_num;
        }
};
// 静态成员属性存储在 .data 段,所以必须 在编译时 分配空间
int T::g_num  =1;

4.2 静态成员的本质

使用如下代码反汇编

  4 class T{
  5     public:
  6         static int g_num;
  7         static int get_num()
  8         {
  9             return g_num;
 10         }
 11 };
 12
 13 class A{
 14     public:
 15         static int g_num;
 16         static int get_num()
 17         {
 18             return g_num;
 19         }
 20 };
 21
 22 int T::g_num  =1;
 23 int A::g_num  =1;
 24 int g_num = 1;
 25
 26 static int get_num()
 27 {
 28     return g_num;
 29 }

查看 g_num, T::g_num, A::g_num

>> 173 08049af4 g     O .data  00000004              _ZN1T5g_numE
>> 189 08049afc g     O .data  00000004              g_num
>> 190 08049af8 g     O .data  00000004              _ZN1A5g_numE

>> 166 08048678 l     F .text  0000000a              _ZL7get_numv
>> 175 08048771  w    F .text  0000000a              _ZN1T7get_numEv
>> 197 0804877b  w    F .text  0000000a              _ZN1A7get_numEv

说明 编译器对符号进行重命名以实现封装
T::g_num --> _ZN1T5g_numE
A::g_num --> _ZN1A5g_numE
g_num --> g_num

T::get_num() --> _ZN1T7get_numEv()
A::get_num() --> _ZN1A7get_numEv()
get_num() --> _ZL7get_numv()

所以以上cpp可用c描述成

 22 // class T
 23 static int T_g_num = 1;
 24 static int T_get_num()
 25 {
 26     return T_g_num;
 27 }
 28
 29 // class A
 30 static int A_g_num = 1;
 31 static int A_get_num()
 32 {
 33     return A_g_num;
 34 }
 35
 36 // normal
 37 static int g_num = 1;
 38 static int get_num()
 39 {
 40     return g_num;
 41 }

结论:
静态成员 本质 是全局函数和变量,无需定义对象,就能可以直接使用。
由于被cpp编译器重命名,所以必须用 加类前缀的方式调用。

4.3 静态成员函数中为什么不能使用普通成员

静态成员函数中使用 普通成员 会报错,如下

 13 class A{
 14     public:
 15         static int g_num;
 16         static int get_num()
 17         {
 18             f();
                m_a++;
 19             return g_num;
 20         }
 21         void f() { get_num(); m_a++;  }
            void f2() {f();  }
        int m_a;
 22 };

因为编译时普通成员没有被分配内存。
如上若翻译成c

 16         static int A_get_num()
 17         {
 18             A_f(this); // 错误,因为 A_get_num 没有this变量
                this->m_a++;  // 错误
 19             return A_g_num;
 20         }
 21         void A_f(A *this) { A_get_num(); this->m_a++; } // 正确
            void A_f2(A *this) {f(this);} // 正确

5. 面向对象实现本质

class A{
        public:
                static int g_num;
                static int get_num() {
                        return g_num;
                }
                int f() {
                        return m_a;
                }
        private:
                int m_a;
};

int A::g_num  =1;

int main()
{
        A::get_num();
        A a;
        a.f();
        return 0;
}

翻译成 c

struct A{
        int m_a;
};

static int A_g_num;
static int A_get_num() {
        return A_g_num;
}
int A_f(struct A *this) {
        return this->m_a;
}

int main()
{
        A_get_num();

        struct A a;
        A_f(&a);
        return 0;
}
cpp