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 T;  // 调用 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;
}

0. 友元

友元机制,让用户在无法获得类的 .cpp 文件情况下,通过修改 .h 文件,声明友元,从而获得修改类私有属性的能力。
friend 声明在 class{} 内,与 private public protect 无关。

0.1 友元函数

class A {
  friend void func(A &a);
private:
  int a;
};

void func(A &a)
{
  a.a++;
}

0.2 友元类

class A {
friend class B;
private:
  int a;
};

class B {
public:
 B(int a) { a.a = a; }
private:
 A a;
}

0.2 友元类

1. 运算符重载

1.1 本质

T operator+(T &t1, T &t2)
{
   T t3(t1.a + t2.a);
   return t3;
}

运算符重载本质是函数,cpp编译器给程序员一个机制,定义运算符相关的函数。

1.2 详细讨论

  1. 重载 二元运算符
    objL op objR
    重载为成员函数
    objL.operator op (objR);
    重载为友元函数
    operator op (objL, objR);

示例

                // t1 + t2 + t3;
                T &operator+(T &t) {
                        a += t.a;
                        b += t.b;
                        return *this;
                }

                // t1 = t2 = t3;
                T &operator=(const T t) {
                        a = t.a;
                        b = t.b;
                        return *this;
                }

                // ++t1;
                T& operator++() {
                        a++;
                        b++;
                        return *this;
                }

                // t1++
                const T operator++(int) {
                        T t = *this;
                        a++;
                        b++;
                        return t;
                }

class T{
        friend ostream &operator<<(ostream &out, T &t);
        friend istream &operator>>(istream &in, T &t);
        ....
};

ostream &operator<<(ostream &out, T &t)
{
        out << t.a << " " << t.b << endl;
        return out;
}

istream &operator>>(istream &in, T &t)
{
	in >> t.a >> t.b;
	return in;
}

操作符重载练习——mystring

#include <iostream>
#include <assert.h>
#include <string.h>
#include <string>
#include <alloca.h>
#include <stdlib.h>
using namespace std;

#define MYSTR_SIZE 256

class mystring {
	friend istream & operator>>(istream &in, mystring &s);
	friend ostream & operator<<(ostream &in, mystring &s);
	public:
		~mystring() {
			delete []data;
		}

		mystring(const char *s = "") {
			len = strlen(s);
			size = (len / MYSTR_SIZE + 1) * MYSTR_SIZE;
			data = new char [size];
			strncpy(data, s, len);
			data[len] = 0;
		}

		mystring(mystring &s) {
			len = s.len;
			size = (len / MYSTR_SIZE + 1) * MYSTR_SIZE;
			data = new char [size];
			strncpy(data, s.data, len);
			data[len] = 0;
		}

		bool operator<(mystring &s) {
			if (len < s.len)
				return true;
			else if (len > s.len)
				return false;
			return strcmp(s.data, data) > 0 ? false : true;
		}

		bool operator>(mystring &s) {
			if (len > s.len)
				return true;
			else if (len < s.len)
				return false;
			return strcmp(s.data, data) > 0 ? true : false;
		}

		bool operator==(mystring &s) {
			if (s.len != len)
				return false;
			return strcmp(s.data, data) == 0 ? true : false;
		}

		bool operator!=(mystring &s) {
			if (s.len != len)
				return true;
			return strcmp(s.data, data) == 0 ? false : true;
		}

		mystring& operator=(mystring &s) {
			if (s.len >= size) {
				size = (s.len/MYSTR_SIZE + 1) * MYSTR_SIZE;
				delete []data;
				data = new char[size];
			}
			strncpy(data, s.data, s.len);
			len = s.len;
			data[len] = '\0';

			return *this;	
		}

		char &operator[](int i) {
			assert(i < len);
			return data[i];
		}
	private:
		char *data;
		int len;
		int size;
};

istream & operator>>(istream &in, mystring &s) {
	char c, *p;

	if (s.len != 0)
		s.len = 0;

	
	while ((c = in.get()) != '\n' ) {
		if (s.len >= s.size) {
			s.size = s.size + MYSTR_SIZE;
			p = new char [s.size];
			strncpy(p, s.data, s.len);
			delete [] s.data;
			s.data = p;
		}
		s.data[s.len++] = c;
	}
	s.data[s.len] = '\0';
	
	return in;
}

ostream & operator<<(ostream &out, mystring &s) {
	if (s.data == NULL || s.len == 0)
		return out;
	cout << s.data;

	return out;
}

int main()
{
	mystring a;
	mystring b("abc");
	mystring c = b;

	cin >> a;
	cout << "a : " << a << endl;
	cout << "b : " << b << endl;
	cout << "c : " << c << endl;

	if (a == b)
		cout << "a == b" << endl;
	if (a != b)
		cout << "a != b" << endl;
	if (a > b)
		cout << "a > b" << endl;
	if (a < b)
		cout << "a < b" << endl;

	a = b;
	cout << "a : " << a << endl;
	cout << "b : " << b << endl;

	a[1] = '1';
	a[2] = '2';
	cout << a << endl;

	return 0;
}

posted on 2022-05-10 22:51  开心种树  阅读(64)  评论(0编辑  收藏  举报