1、main()函数不一定是程序中第一个执行的函数
#include <iostream>
using namespace std;
class A{
public:
A()
{
cout<<"In default A's constructor"<<endl;
}
};
A a;
int main()
{
cout<<"In main()"<<endl;
return 0;
}
实际上,所有类外对象的构造函数都是先于main()函数执行的。如果要对类中的对象进行初始化,那么这些对象的构造函数也是在main()函数之前执行的。
2、函数参数入栈方式与调用约定
(1) 调用约定简介
C/C++中常见的调用规范有__cdecl、__stdcall、__fastcall和__thiscall。
3、函数调用时栈的变化情况
4、如何禁止函数传值调用
按照参数形式的不同,C++应有3种函数调用方式:传值调用、引用调用和指针调用
传值调用在进入函数体前,会在栈上建立一个实参的副本,而引用和指针调用没有这个动作。
建立副本的操作是利用拷贝构造函数进行的。因此要禁止传值调用,就要在类的拷贝构造函数上做文章。
(1) 不显式定义拷贝构造函数不可行,编译器会生成默认拷贝构造函数
(2) 显式定义拷贝构造函数,并将访问权限设置为private
(3) 关于拷贝构造函数的说明
A、拷贝构造函数A(const A& ){ }, 拷贝构造函数的参数必须是一个引用或一个指针,传值的方式会调用该类的拷贝构造函数,造成无穷递归拷贝构造函数
B、拷贝构造函数参数通常是const的,但是const并不是严格必须的
C、在下面几种情况会调用拷贝构造函数
a、显示或隐式的地用同类型的一个对象来初始化另外一个对象
b、作为实参以值传递
c、在函数体内返回一个对象时
d、需要产生一个临时类对象时(类对象作为函数返回值会创建临时对象)
5 、函数指针
1、简单回顾函数指针用法
#include <iostream>
using namespace std;
int add(int i,int j)
{
return i + j;
}
int main()
{
//1
int (*pFun)(int,int) = add;
int tmp = pFun(2,3);
//2
typedeff int (*AddP)(int,int);
AddP funcP= add;
tmp = funcP(2,3);
cout<<tmp<<endl;
}
注意:
A、常借助于typedef类型定义简化函数指针的定义
B、函数名代表函数的入口地址,在为函数指针赋值时,funcp = add 和funcp = &add都是正确的
C、“回调函数”可以理解成通过函数指针调用的函数
函数指针作为参数传递
#incluce <iostream>
using namespace std;
int f()
{
return 1;
}
//显式
void invoke0(int (*func)())
{
cout<<(*func)()<<endl;
}
//隐式
void invoke1(int func())
{
cout<<func()<<endl;
}
int main()
{
invoke0(f);
invoke1(f);
return 0;
}
2、 指向类成员函数的函数指针用法
(1)函数指针指向类静态成员
函数指针 = 类名::函数名
C++沿用C语言中对函数指针的定义和使用规范,对于类静态成员函数,可以理解为“作用域受限的外部函数”
(2)函数指针指向类非静态成员
函数指针要以 对象的“成员指针”的形式定义和赋值。
6、操作符重载
1、输入、输出操作符重载
输入、输出操作符重载都只能采用友元函数的形式进行,但不能将operator>>() 或operator<<()声明为istream类或ostream类的成员函数。
这是因为其是C++标准库的类,不能被用户随意修改。所以,要从标准输入对象、输出对象将数据读入类或从someClass的对象输出到标准
输出对象,只能重载为全局函数。
ostream& operator <<(ostream&, const someClass& );
istream& operator >>(istream&, someClass& );
//举例
#include <iostream>
using namespace std;
class Complex
{
private:
double real;
double image;
public:
Complex(double r=0,double i=0):real(r),image(i){ }
friend ostream& operator<<(ostream&, const Complex& );
friend istream& operator>>(istream&, Complex& );
};
ostream& operator<<(ostream& o, const Complex& c)
{
o<<c.real<<"+"<<c.image<<"i";
return 0;
}
istream& operator>>(istream& i, Complex& c) //注意不能加const
{
}
int main()
{
return 0;
}
注意:
(1)对于输入、输出操作符重载,只能采用友元函数形式,而不能采用成员函数形式
(2)输出比较容易,输入需要做容错处理,详细可参考《cin的详细用法》
2、赋值操作符重载
将两个同类型变量的值从一端(右端)传到另一端(左端)。但以下两种情况下需要重载赋值操作符:
1)赋值号两边的表达式不同,且无法进行类型转换
2)需要进行深拷贝
赋值操作符智能通过类的成员函数的形式重载。
注意:
1)由于深拷贝会涉及内存的动态分配和释放等一些较为复杂的操作,所以程序员在编写自定义类时要尽量避免深拷贝的出现。
可以将类中的字符串定义为string name; 而不使用char *name,就可以避免自己编写实现深拷贝的代码。实际的深拷贝工作
是由string类来完成的,而string类是由C++标准库提供的,所以我们可以放心使用
2)对赋值操作符进行重载时,通常将操作符的返回值定义为赋值值操作数类型的引用,可以实现对赋值表达式的求值和链式操作。
7 、函数重载、隐藏、覆盖和重写的区别
7.1 函数重载
1)函数重载定义
同一作用域中,当同名函数的形式参数(参数个数、类型或者顺序不同时),构成函数重载
2)注意事项:
a) 函数返回值类型与构成函数重载无任何关系;
b) 类的静态成员函数与普通成员函数可以形成重载;
c) 函数重载发生在同一作用域,如类成员函数之间重载、全局函数之间重载
7.2 函数隐藏