1.类成员函数的定义和实现可以分开写
//定义一个类
class ABC
{
public:
void hello(void);
};
//实现类方法
void ABC::hello()
{
cout<<"hello world"<<endl;
};
2.类成员的可访问范围
- private:私有成员,只能在成员函数内部访问
- protected:保护成员
- public:公有成员,可以在任何地方访问
- 如果属性和方法的权限是缺省的,则默认是私有
- 在类的成员函数内部:
- 可以访问当前对象的全部属性、方法,可以访问同类对象的全部属性、方法
- 在类的成员函数以外,只能访问对象的公有方法
- 设置私有成员的机制,叫做“隐藏”
- “隐藏”的目的是强制对成员变量的访问一定要通过成员函数进行,那么以后成员变量的类型等属性修改后,只需要更改成员函数即可。否则所有直接访问成员变量的语句都要修改。
3.成员函数的重载和参数缺省
使用缺省参数要避免重载时候的二义性
class ABC
{
public:
void hello(void);
void hello(std::string name, int age=20);
};
void ABC::hello()
{
cout<<"hello world"<<endl;
};
void ABC::hello(std::string name, int age)
{
cout<<"hello world "<<name<<",age="<<age<<endl;
};
int main(void)
{
ABC obj;
obj.hello("jack"); //输出结果 hello world jack,age=20
//system("pause");
return 0;
}
4.构造函数
- 它是成员函数的一种
- 名字与类名相同,可以有参数,不能有返回值(void也不用写)
- 作用是对象初始化,如给成员变量赋值
- 如果没有定义构造函数,则编译器自动生成一个无参的构造函数,不进行任何操作
- 如果定义了构造函数,则编译器不生成默认的无参数的构造函数
- 对象生成时,构造函数被自动调用。对象一旦生成,就再也不能调用构造函数
- 构造函数可以重载
- 构造函数执行必要的初始化工作,有了构造函数,就不必专门再写初始化函数
- 有时对象没有初始化就被调用,会导致程序出错
class Test {
public:
Test( int n) { } //(1)
Test( int n, int m) { } //(2)
Test() { } //(3)
};
Test array1[3] = { 1, Test(1,2) }; // 三个元素分别用(1),(2),(3)初始化
Test array2[3] = { Test(2,3), Test(1,2) , 1}; // 三个元素分别用(2),(2),(1)初始化
Test * pArray[3] = { new Test(4), new Test(1,2) }; //两个元素分别用(1),(2) 初始化
5.复制构造函数
只有一个参数,即对同类对象的引用,不能是对象
形如:X::X(X&) 或者 X::X(const X&),二者选一,后者能以常量对象作为参数
如果没有定义复制构造函数,那么编译器生成默认复制构造函数,默认的赋值构造函数完成赋值功能
//默认的复制构造函数
class Complex {
private :
double real,imag;
};
Complex c1; // 调用缺省无参构造函数
Complex c2(c1); // 调用缺省的复制构造函数 ,将 c2 初始化成和 c1 一样
//如果自定义复制构造函数,则默认的复制构造函数不存在
class Complex {
public :
double real,imag ;
Complex(){ }
Complex( const Complex & c )
{
real = c.real;
imag = c.imag;
cout << “Copy Constructor called”;
}
};
Complex c1 ;
Complex c2(c1);
复制构造函数起作用的三种情况
- 当用一个对象去初始化同类的另一个对象时
Complex c2(c1);
Complex c2 = c1; //用复制构造函数初始化,不是赋值语句
- 如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用
- 如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数将被调用
class A
{
public:
int v;
A() {}; //空的构造函数
A(int value) {v = value}; //有一个参数的构造函数
A( A & a) { //复制构造函数
cout<<"Copy constructor called"<<endl;
}
};
void Func (A a1){ }
A Func (){
A b(4);
return b;
}
int main(){
A a2;
Func (a2); //a2是实参,a1是形参,所以参数传递时,需要调用复制构造函数
cout<<Func().v<<endl; //打印返回值对象的属性
return 0;
}
注意,对象间的赋值并不导致复制构造函数被调用
class A...;
A a,b; //创建了2个对象
A c(a); //通过拷贝构造函数来创建新的对象
a = c; //仅仅只是完成对象属性之间的赋值(浅拷贝),不会调用拷贝构造函数
//需要注意指针的赋值会指向同一款区域,某个对象释放空间后,这个指针将变成野指针
6.常量引用参数的使用
void fun(A obj)
{
cout<<"fun"<<endl;
}
这样的函数,调用时会调用复制构造函数,开销比较大
可以使用A&引用类型作为参数,如果不希望值被改变,还可以加const
void fun(const A& obj)
{
cout<<"fun"<<endl;
}
7.类型转换构造函数
定义转换构造函数的目的是实现类型的自动转换
只有一个参数,而且不是复制构造函数的构造函数,一般可以看做是转换构造函数
当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象
class Complex {
public:
double real, imag;
Complex( int i) { // 类型转换构造函数
cout << "IntConstructor called"<<endl;
real = i; imag 0;
}
Complex(double r,i) {real = r; imag i; }
};
int main ()
{
Complex c1(7,8);
Complex c2 = 12;
c1 = 9; // 9被自动转换成一个临时 Complex 对象,然后赋值给c1
cout << c1.real "," imag endl;
return 0;
}
8.析构函数
名字与类名相同,在前面加~,没有参数和返回值,一个类最多只能有一个析构函数
析构函数在对象消亡时自动调用,用以善后工作,例如空间的释放
如果定义类时缺省析构函数,则编译器自动生成析构函数,里面啥也不做
如果定义了析构函数,则编译器不再自动生成缺省的析构函数
delete运算符会引起析构函数的调用
class A
{
public:
int value;
public:
~A(){cout<<"distory"<<endl;}
};
//main函数运行,只会打印一次,new分配的空间,不delete是不会调用析构函数的
int main(void)
{
A * p = new A; //程序结束时不会调用析构函数
A a; //程序结束时会调用析构函数
return 0;
}