C++面向对象

此博文仅作为C++考研专业课的复习内容。

面向对象

构造函数

在对象被创建的时候将自动调用。

复制构造函数

形参是本类对象的引用。其作用是使用一个已经存在的对象,去初始化一个同类的新对象。

复制构造函数在以下三种情况会被调用。

  1. 当用类的一个对象去初始化该类的另一个对象。
Point a(1,2);
//两种写法都会调用复制构造函数,只是写法上不同。
Point b(a);//用对象a初始化b
Point c = a;////用对象a初始化b
  1. 如果函数的形参是类的对象,调用函数时,进行形参和实参结合时。
void foo(Point p){
cout<<p.getX()<<endl;
}
int main(){
Point a(1,2);
foo(a);
return 0;
}

注:值传递时调用复制构造函数,传引用时不会调用。所以传递比较大的对象时,传递引用效率会更高。

3. 如果函数的返回值是类的对象,函数执行完成返回调用者时。

Point foo(){
Point a(1,2);
return a ;
}
int main(){
Point b;
b= foo();
return 0;
}

注:a离开函数foo()时,a对象会消亡,此时会调用复制构造函数创建一个无名的临时对象存在于表达式b= foo()中。

析构函数

析构函数是用来完成对象被删除前的一些清理工作,是在对象的生存期即将结束的时候被自动调用的。

析构函数的调用执行顺序与构造函数刚好相反。

类的组合

  • 当创建类的对象时,会先初始化类中的其他对象和基本数据类型的成员变量。构造函数按以下顺序调用:
  1. 调用内嵌对象的构造函数。用顺序按照内嵌对象在组合类中定义的顺序。注意,内嵌对象在构造函数的初始化列表中出现的顺序与内嵌对象构造函数的调用顺序均无关。
  2. 执行本类构造函数的函数体。
  • 析构函数的调用执行顺序与构造函数刚好相反。析构函数的函数体执行完成后,内嵌对象的析构函数一一执行。内嵌对象的析构函数调用顺序和他们在组合类中的类定义中出现的次序刚好相反。
  • 类的拷贝构造函数编写。需要为内嵌成员对象的复制构造函数传递参数。
class Point{
public:
Point(Point& p);
};
class Line{
private:
Point p1,p2;
public:
Line(Point& p1,Point& p2);
};
Line::Line(Point& xp1,Point& xp2):p1(xp1),p2(xp2){
//to-do something
}

前向引用申明

class B; //前向引用申明
class A{
public:
void foo(B b);
};
class B{
public:
void bar(A a);
};

当类发生循环依赖时,需要使用前向引用申明。但是即便是存在前向引用申明,但是无法直接定义类的对象。

类的静态成员

静态数据成员

static修饰。类属性是描述类的所有对象共同特征的数据项,对任何实例,他的属性值是相同的。

class Point{
public:
Point(){
count++;
}
void show_count(){
cout<<count<<endl;
}
private:
static int count;
};
int Point::count = 0;

静态数据成员单独在类外初始化,需要专门为他们分配空间。

静态函数成员

静态成员函数可以直接访问该类的静态数据和函数成员。而访问非静态成员必须通过对象名。

class A{
public:
static void foo(A a);
private:
int x;
};
void A::foo(A a){
cout<<x; //对x的引用时错误的。
cout<<a.x; //正确。
}

友元

友元关系提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。

友元的特点

  • 友元关系无法传递。
  • 友元关系是单向的。
  • 友元关系不能被继承。

友元函数

友元函数是在类中用关键字friend修饰的非成员函数。可以是普通函数,也可以是其他类的成员函数。类的友元函数可以通过对象名访问类的私有和保护成员。

class Point{
Point(int x,int y);
int getX();
int getY();
friend float dist (Point &p1,Point &p2);
private:
int x,y;
};
float dist(Point &p1,Point &p2){
double x = p1.x - p2.x;
double y = p1.y - p2.y;
return static_cast<float>(sqrt(x * x + y * y));
}
int main(){
Point p1(1,1),p2(4,5);
cout<<dist(p1,p2);
return 0;
}

友元类

同友元函数一样,一个类也可以将另一个类声明为友元类。若A类为B类的友元类,则A类的所有成员都是B类的友元函数,都可以访问B类的私有和保护成员

class A{
public:
int getX();
friend class B; //B是A的友元类
private:
int x;
};
class B{
public:
void set(int i);
private:
A a;
};
void B::set(int i){
a.x = i; //由于B是A的友元类,所以在B的成员函数中可以访问A类对象的私有成员。
};

共享数据的保护

常对象

数据成员值在对象的整个生存期间内不改变。常对象必须进行初始化,而且不能被更新。

const A a(3,4);

用const修饰的类成员

  1. 常成员函数

类型说明符 函数名 (参数列表) const

eg: void foo() const;

  • 定义和申明时都要带const关键字。
  • 常对象只能调用常成员函数。
  • 无论是否通过常对象调用常成员函数,在函数调用期间,目的对象都被视为常对象,因此常成员函数不能更新目的对象的数据成员,也不能针对目的对象调用其非const修饰的函数。
class A {
public :
void foo() const{
cout<<"foo const"<<endl;
this->bar();//error C2662: “A::bar”: 不能将“this”指针从“const A”转换为“A &
this->x = 1;//error C3490: 由于正在通过常量对象访问“x”,因此无法对其进行修改
}
void bar(){
cout<<"bar"<<endl;
}
private :
int x;
};
  • const可以区分函数的重载。例如申明如下函数。

void print();

void print() const;

此时常对象会调用第二个,非常对象就近调用第一个。
  1. 常数据成员

    常数据成员只能通过构造函数的初始化列表来指定具体值
class A {
public :
A(int i):a(i){
//a = i; //error
}
private :
const int a;
static const int b;
//static const int b = 10; //正确 int或enum的静态常量可在类中直接指定值。
};
const int A::b = 10;
  1. 常引用

    所引用的对象无法更新。

    需要注意的是:
  • 非const的引用只能绑定到普通对象,但常引用可以绑定到常对象。
  • 常引用对象,通过该引用访问时,只能把对象当做常对象。(不能修改数据成员和调用非const成员函数)
上一篇:【转】OpenStack和Docker、ServerLess能不能决定云计算胜负吗?


下一篇:linux 目录分类与文件操作