C++ 类和对象

内联函数和重载函数

内联函数是C++为降低小程序调用开销的一种机制。仅在函数原型作一次声明,适用只有1~5行的小函数,不能含有复杂结构控制,不能递归调用。

inline int f( ) ;
void main( )
{
    a = f();
    ... 
}
int f( )
{
    ...
}

函数重载是以同一个名字命名多个函数实现。

int max(int a, int b);
int max(int a, int b, int c);
double max(double a, double b);

对象的构造和析构

拷贝构造

ConstructorCopy::ConstructorCopy()
{
	cout << "default constructor called " << endl;
}

ConstructorCopy::ConstructorCopy(const ConstructorCopy &c)
{
	cout << "copy constructor called" << endl;
}

ConstructorCopy::~ConstructorCopy()
{
	cout << "destory constructor called" << endl;
}
...
int main()
{   ...
    ConstructorCopy cc1;
    ConstructorCopy cc2(cc1); //拷贝构造,打印 copy constructor called
    ConstructorCopy cc3 = cc; //拷贝构造,打印 copy constructor called
    ConstructorCopy cc4;
    cc4 = cc;                 //赋值构造,调用默认构造函数
}

匿名对象

如果函数的返回值是一个元素(复杂类型的),则返回一个新的匿名对象(会调用匿名对象类的copy构造函数)

ConstructorCopy g()
{
    ConstructorCopy cc;
    return cc;  //cc被析构,返回一个新的匿名对象
}

如果用匿名对象去 初始化 另一个同类型的对象,匿名对象“实名”,不被析构
如果用匿名对象去 赋值 另一个同类型的对象,匿名对象被析构

void obj1()
{
    ConstructorCopy m = g();
}
void obj2()
{
    ConstructorCopy n;
    n = g();    //匿名对象被析构
}

构造函数中调用重载构造函数是危险的

A::A(int _a)
{
    this->a = _a;
    A(_a, 100);
}
A::A(int _a, int _b)
{
    this->a = _a;   this->b = _b;
}
void test()
{
    A t(10);
    cout << t.get_b() << endl;
}

调用完 test() 后,打印 b 的值发现是乱码,因为调用的 A(_a, 100) 存储的是匿名对象的值,和对象 t 的成员变量的值是没有关系的。

深拷贝和浅拷贝

typedef unsigned int u32;
class StoreString
{
public:
	StoreString();
	StoreString(const char *c);
	~StoreString();

private:
	char *p;
	u32 len;
};

StoreString::StoreString(const char *c)
{
	len = strlen(c);
	p = (char *)malloc(len + 1);
	strcpy(p, c);
}

StoreString::~StoreString();
{
	if (p != NULL) {
		free(p);
		p = NULL;
		len = 0;
	}
}

void obj3()
{
    StoreString s0("abcdefg");
    StoreString s1 = s0;
}

int main()
{
    obj3();
    ...
}
C++ 类和对象

语句 StoreString s1 = s0; 使用 c++ 编译器提供的默认拷贝函数初始化 s1s0s1p 指针都指向同一块堆内存,在 obj3() 执行完毕后,s0 对象析构,p 指向的堆内存释放,s1 对象析构,再次释放 p 指针指向的内存空间,程序错误,出现 coredump。解决方法:手动编写拷贝构造函数,使用深拷贝。

StoreString::StoreString(StoreString &s)
{
	len = s.len;
	p = (char *)malloc(len + 1);
	strcpy(p, s.p);
}

构造函数初始化列表

class A
{
public:
	A();
	A(int _a);
	~A();
private:
	int a;
};
A::A() {}
A::A(int _a)
{
	a = _a;
}
A::~A() {}

class B
{
public:
	B(int _a, int _b);
	~B();
private:
	A a0, a1;
	int a, b;
};

B::B(int _a, int _b):a1(_b),a0(_a)
{
	a = _a;
	b = _b;
}

B::~B() {}

B 构造函数初始化列表对A类成员变量 a0, a1 进行初始化,B::B(int _a, int _b):a0(_a),a1(_b) {}.
执行顺序:先执行被组合对象的构造函数,如组合对象有多个,则按照定义顺序执行,即先执行 a0(_a)a1(_b),再执行 B 内部的 a = _a; b = _b;。析构函数和构造函数的调用顺序相反。

new 和 delete 操作符

  • new 运算符动态分配堆内存
  • delete 运算符释放已分配的内存空间
int *p1 = new int;
char *p2 = new char;
int *p3 = new int[4];  //对应 c 中 int *p3 = (int *)malloc(sizeof(int) * 4);
int *p4 = new int(10); //*p4 初值 10
...
delete p1;
delete p2;
delete []p3;

对复杂类型 (class) 进行 new 和 delete 时会调用对象的构造和析构函数,malloc 和 free 并不会调用。

this 指针

总是指向对象本身,作用域在类内部,写与不写,效果一样。更深层次的理解是,c++编译器将构造函数转化,添加一个参数,即指向该类的this指针。

静态成员和静态成员函数

类中静态成员表明其被该类所拥有,而不是某个对象。
静态成员函数内不能使用普通变量和普通成员函数,因为编译器不知道该普通变量属于哪个对象,例:

class AA
{
public:
    AA();
    static void print_bb();
	~AA();
private:
	int bb;
};
void AA::print_bb()
{
	cout << "bb: " << bb << endl;
}
void test_static()
{
    AA a0, a1, a2;
    AA::print_bb();
}

test_static() 在进行匿名调用时,即调用 AA::print_bb() ,编译器并不知道打印哪一个对象的bb值。当然在VS IDE中写该代码时也会报错。

类中 const 修饰函数到底修饰的什么

class BB
{...
public:
    void Operate(int a, int b) const;
}

const 写什么位置没有关系,实际上它修饰的是隐藏的 this 指针。

函数返回元素和引用

Test& add(Test &t)  //返回一个引用 相当于返回自身
{
    this->a = this->a + t.getA();
    this->b = this->b + t.getB();
    return *this;
}
Test add2(Test &t)
{
    Test t1(this->a + t.getA(), this->b + t.getB());
    return t1;
}
void test()
{
    Test t1(1, 2);
    Test t2(3, 4);
    Test t3 = t1.add2(t2);  // t3 = t1 + t2;
    t1.add(t2);     //相当于 t1 = t1 + t2;
    
}

友元

友元函数

class FriendF
{
public:
	FriendF();
	~FriendF();
	friend void operate_a(FriendF *f, int _a); //friend声明友元函数,声明位置private/public无关
private:
	int a;
};
FriendF::FriendF(){}
FriendF::~FriendF(){}
void operate_a(FriendF *f, int _a)  //通过指针访问私有变量
{
	f->a = _a;
}

友元类

  • 若B是A的友元类,则B类所有成员函数都是A类的友元函数
  • 通常设计为一种对数据操作或类之间传递消息的辅助类
class FriendA
{
	friend class FriendB;
public:
	void display() { cout << x << endl; };
private:
	int x;
};
class FriendB
{
public:
	void set(int i) { a_object.x = i; }	//通过类成员访问 FriendA 的私有成员数据
	void display() { a_object.display(); }
private:
	FriendA a_object;
};
void test()
{
	FriendB fb;
	fb.set(10);
	fb.display();
};

友元在一定程度上破坏了类的封装性,为什么还会存在?
参照 java 反射机制,在有些应用场景中,需要从字节码中获取对象,直接修改私有变量,c++ 提供了友元函数友元类来修改对象私有成员数据,因为从生成的汇编代码里面找是很困难的。