内联函数和重载函数
内联函数是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();
...
}

语句 StoreString s1 = s0;
使用 c++ 编译器提供的默认拷贝函数初始化 s1
,s0
和 s1
的 p
指针都指向同一块堆内存,在 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++ 提供了友元函数友元类来修改对象私有成员数据,因为从生成的汇编代码里面找是很困难的。