c++类&对象

1.类成员函数

class Box
{
   public:
      double length;         // 长度
      double breadth;        // 宽度
      double height;         // 高度
      double getVolume(void);// 返回体积
};

//类的成员函数
double Box::getVolume(void) { return length * breadth * height; }

成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义。
函数如果直接在类中定义的话,则该函数默认为内联的(不太确定)。

2.类访问修饰符

public: // 公有成员
 
   公有成员在程序中类的外部是可访问的。您可以不使用任何成员函数来设置和获取公有变量的值
 
protected: // 受保护成员
 
   保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。
 
private:// 私有成员
 
   私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。

   默认情况下,类的所有成员都是私有的

如果继承时不显示声明是 private,protected,public 继承,则默认是 private 继承,在 struct 中默认 public 继承

继承方式

3.构造函数&析构函数
构造函数(有参和无参):

       类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

       构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值

  使用初始化列表来初始化字段

Line::Line( double len): length(len)
{
    cout << "Object is being created, length = " << len << endl;
}
//上面的语法等同于如下语法:
Line::Line( double len) { length = len; cout << "Object is being created, length = " << len << endl; }
//假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}


析构函数:

       类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

       析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

4.c++拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

      • 通过使用另一个同类型的对象来初始化新创建的对象。

      • 复制对象把它作为参数传递给函数。

      • 复制对象,并从函数返回这个对象。

如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。

//拷贝构造函数的最常见形式
classname (const classname &obj) {
   // 构造函数的主体
}
#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      int getLength( void );
      Line( int len );             // 简单的构造函数
      Line( const Line &obj);      // 拷贝构造函数
      ~Line();                     // 析构函数
 
   private:
      int *ptr;
};
 
// 成员函数定义,包括构造函数
Line::Line(int len)
{
    cout << "调用构造函数" << endl;
    // 为指针分配内存
    ptr = new int;
    *ptr = len;
}
 
Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}
 
Line::~Line(void)
{
    cout << "释放内存" << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}
 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}
 
// 程序的主函数
int main( )
{
   Line line1(10);
 
   Line line2 = line1; // 调用了line2的拷贝构造函数,line2还没有构造

  //Line line3(20);
  //line3=line1; //这个地方才调用重载运算符!!!line3已经构造初始化过了
display(line1); //对象作为参数传递给函数也会调用拷贝构造函数,不再调用构造函数 display(line2); return 0; }

当上面的代码被编译和执行时,它会产生下列结果:

调用构造函数                                   //line1的构造函数
调用拷贝构造函数并为指针 ptr 分配内存             //line2的拷贝构造函数
调用拷贝构造函数并为指针 ptr 分配内存             //display(line1)先调用拷贝构造,深拷贝一份line1
line 大小 : 10                               //display(line1)内容打印大小
释放内存                                      //display(line1)函数执行完,局部深拷贝拷贝出来的对象执行析构
调用拷贝构造函数并为指针 ptr 分配内存             //同理display(line2)
line 大小 : 10
释放内存
释放内存                       //最后两个释放为line2和line2执行的析构函数         
释放内存

 总结:拷贝构造其实也是构造函数,在调用拷贝构造的三种情况下,无非就是通过深拷贝复制数据内容,但是新对象的地址是变的(记住拷贝构造也是构造函数)。可能有的小伙伴有误解,拷贝构造不是实现用的引用吗,新对象的地址应该没有变化啊。其实这个里面有一个误区,就是拷贝构造的引用其实是为了深拷贝原对象数据内容,这里的引用可以简单的认为就是使用原对象,而新对象其实已经开辟空间地址了(细探类开辟空间)。不信你看看构造函数,它也没有专门给开辟首地址。

拷贝构造函数和赋值的区别

  • 用一个已存在的对象去构造一个不存在的对象(构造之前不存在),就是拷贝构造.
  • 用一个已存在的对象去覆盖另一个已存在的对象,就是赋值运算.
  • 拷贝构造函数从一个已经存在的变量来初始化一个新声明的变量,不需要清除现有的值(因为是新创建,所以没有现有值)
  • 拷贝构造函数没有返回值。
  • 赋值运算符return *this.(This is necessary to allow multiple assignment, eg x = y = z;)

5.c++友元函数

  类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

  友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:

class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );  //在类中声明了,但是并不是类的成员函数
   void setWidth( double wid );
};

  声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:

friend class ClassTwo;

使用友元函数注意的要点:

  1. 类中通过使用关键字friend 来修饰友元函数,但该函数并不是类的成员函数,其声明可以放在类的私有部分,也可放在共有部分。友元函数的定义在类体外实现,不需要加类限定。
  2. 一个类中的成员函数可以是另外一个类的友元函数,而且一个函数可以是多个类友元函数。
  3. 友元函数可以访问类中的私有成员和其他数据,但是访问不可直接使用数据成员,需要通过对对象进行引用
  4. 友元函数在调用上同一般函数一样,不必通过对对象进行引用。

例子:

#include <iostream>

using namespace std;

class Box
{
    double width;
public:
    friend void printWidth(Box box);
    friend class BigBox;
    void setWidth(double wid);
};

class BigBox
{
public :
    void Print(int width, Box &box)
    {
        // BigBox是Box的友元类,它可以直接访问Box类的任何成员
        box.setWidth(width);
        cout << "Width of box : " << box.width << endl;
    }
};

// 成员函数定义
void Box::setWidth(double wid)
{
    width = wid;
}

// 请注意:printWidth() 不是任何类的成员函数
void printWidth(Box box)
{
    /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
    cout << "Width of box : " << box.width << endl;
}

// 程序的主函数
int main()
{
    Box box;
    BigBox big;

    // 使用成员函数设置宽度
    box.setWidth(10.0);

    // 使用友元函数输出宽度
    printWidth(box);

    // 使用友元类中的方法设置宽度
    big.Print(20, box);

    getchar();
    return 0;
}

6.c++内联函数(提升执行效率)
  函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。
  为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数。指定内联函数的方法很简单,只需要在函数定义处增加 inline 关键字。

 #include <iostream>
 using namespace std;
 //内联函数,交换两个数的值
 //注意,要在函数定义处添加 inline 关键字,在函数声明处添加 inline 关键字虽然没有错,但这种做法是无效的,编译器会忽略函数声明处的 inline 关键字。
 inline void swap(int a, int b)
 {
     int temp;
     temp = a;
     a = b;
     b = temp;
    cout<<a<<", "<<b<<endl;
}
 
 int main()
 {
     swap(1,2);
     swap(2,3);
     swap(3,4);
     return 0;
}

  对于上面的程序,每次遇到“swap()”函数时,都会被替换为:“int temp;temp = a;a = b;b = temp;cout<<a<<", "<<b<<endl;”,就像C语言的宏替换一样,这样做的好处是什么呢?

  首先需要注意的是,上面写的“swap()”这个函数是在文件编译时就被替换了,也就是说最终的主函数里面实际上是很多条语句的,因为我调用了那么多次“swap()”函数,每个都被替换成上面绿色部分的那么多语句了,这样的好处就是,程序真正开始运行时,跑到这里时不用再去另外调用“swap()”函数,直接执行被替换的东西就行了,这样以来就省去了电脑在来回调用函数时的开销。

  上面说的内联函数的优点里面,其实同时也说出来了内联函数的缺点,那就是:内联函数你每调用一次,都会被最终替换成那么多语句,实际上就是编译器又为你每次都再把那些语句多写了一次,这样以来,程序的体积就变大了。

  所以,综上所述:一般只将那些短小的、频繁调用的函数声明为内联函数。如果一个函数频繁调用,但是这个函数很长,那就不适合声明为内联函数,要不然每替换一次,就又多出来那么多语句,程序体积就太大了。

7.c++中的this指针

  在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。

  友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。

几点注意:

  • this 是 const 指针,它的值是不能被修改的,一切企图修改该指针的操作,如赋值、递增、递减等都是不允许的。
  • this 只能在成员函数内部使用,用在其他地方没有意义,也是非法的。
  • 只有当对象被创建后 this 才有意义,因此不能在 static 成员函数中使用(后续会讲到 static 成员)。

this到底是什么?

  this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。
  this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。
  在《C++函数编译原理和成员函数的实现》一节中讲到,成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息,所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。这个额外的参数,实际上就是 this,它是成员函数和成员变量关联的桥梁。

8.c++中指向类的指针
  一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。

9.c++类的静态成员
  我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本
  静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化

静态成员函数

  如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。

  静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。

  静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。

静态成员函数与普通成员函数的区别:

  • 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
  • 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
    #include <iostream>
     
    using namespace std;
     
    class Box
    {
       public:
          static int objectCount;
          // 构造函数定义
          Box(double l=2.0, double b=2.0, double h=2.0)
          {
             cout <<"Constructor called." << endl;
             length = l;
             breadth = b;
             height = h;
             // 每次创建对象时增加 1
             objectCount++;
          }
          double Volume()
          {
             return length * breadth * height;
          }
          static int getCount()
          {
             return objectCount;
          }
       private:
          double length;     // 长度
          double breadth;    // 宽度
          double height;     // 高度
    };
     
    // 初始化类 Box 的静态成员
    int Box::objectCount = 0;
     
    int main(void)
    {
      
       // 在创建对象之前输出对象的总数
       cout << "Inital Stage Count: " << Box::getCount() << endl;
     
       Box Box1(3.3, 1.2, 1.5);    // 声明 box1
       Box Box2(8.5, 6.0, 2.0);    // 声明 box2
     
       // 在创建对象之后输出对象的总数
       cout << "Final Stage Count: " << Box::getCount() << endl;
     
       return 0;
    }

    运行结果:

    Inital Stage Count: 0
    Constructor called.
    Constructor called.
    Final Stage Count: 2
原文地址:https://www.cnblogs.com/baconZhang/p/13823113.html