C++面向对象程序设计

面向对象程序 = 类 + 类 + 类 + …
将某类客观事物共同特点归纳出来形成一个数据结构
将这些事物所能进行的行为也归纳出来,形成一个个函数,这些函数可以用来操作数据结构(这一步叫做抽象
通过某种语法形式,将这些数据结构和函数捆绑在一起,形成一个“类”。

类和对象

写一个程序,输入矩形长和宽,输出面积和周长
矩形的属性:长和宽
行为:计算面积,计算周长,设置长和宽

长,宽成为 矩形类的成员变量,三个函数成为成员函数

class CRectangle
{
    public:
        int w,h;   //成员变量
        //下面三个成员函数
        int Area(){
            return w*h;
        }
        int Perimeter()
        {
            return 2*(w+h);
        }
        void Init(int w_,int h_)
        {
            w = w_; h = h_;
        }
};    //必须有分号

int main()
{
    int w,h;
    CRectangle r;
    cin >> w >>h;
    r.Init(w,h);
    cout<<r.w<<endl;
    cout<<r.Area()<<endl<<r.Perimeter();
    return 0;
}

和结构变量一样,对象所占用的内存空间大小等于所有成员变量的大小之和
对于上面的CRectanlge类, sizeof(CRectangle) = 8(因为有两个int类型的成员变量)

CRectangle r1,r2;
CRectangle  * p1 = &r1;
CRectangle *p2 = &r2;
p1->w = 5;
p2->Init(5,4);

类成员函数

//stock00.h
#include<iostream>


#ifndef STOCK00_H_
#define STOCK00_H_

#include<string>

class Stock
{
    private:
        std::string company;
        long shares;
        double share_val;
        double total_val;
        void set_tot() {total_val = share * share_val;}
    
    public:
        void acquire(const std::string & co, long n, double pr);
        void buy(long num,double price);
        void update(double price);
        void show();
};
#endif

关键字private和public描述了对类成员的访问控制,使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数(或者友元函数)
类设计尽可能将公有接口与实现细节分开,公有接口表示设计的抽象组件,将实现细节放在一起并将它们与抽象分开被称为封装

成员函数的定义与常规函数定义非常相似,它们有函数头和函数体,也可以有返回类型和参数,但是他们有两个特殊的特征:

  • 定义成员函数时,使用作用域解析运算符( :: ) 来标识函数所属的类
  • 类方法可以访问类的private组件
    首先,成员函数的函数头使用作用域解析运算符(::)来指出函数所属的类,例如,update()成员函数的函数头如下:
void Stock::update(double price)

构造函数

C++中public、protected及private用法
构造函数
成员函数的一种,执行必要的初始化工作

  • 名字与类名字相同,可以有参数,不能有返回值(void也不行)
  • 作用是对对象进行初始化,如给成员变量赋初值
  • 如果定义类时没有写构造函数,则编译器生成一个默认的无参数的构造函数
    (默认构造函数无参数不做任何操作)
  • 对象生成时构造函数自动被调用,对象一旦生成,就再也不能在其上执行构造函数
  • 一个类可以有多个构造函数
class Complex{
        private:
            double real,imag;
        public:
            void Set(double r,double i);
};  //编译器自动升成默认构造函数

Complex c1;  //默认构造函数被调用
Complex *pc = new Complex;  //默认构造函数被调用
class Complex{
    private:
        double real,imag;
    public:
        Complex(double r,double i = 0);
};
//构造函数
Complex::Complex(double r,double i){
    real=r;imag=i;
}
Complex c1;  //报错,缺少构造函数的参数 
Complex *pc = new Complex;  //报错,缺少参数
Complex c1(2);  //OK
Complex * pc = new Complex(3,4);

整形可以自动转化为double类型
一个类可以有多个构造函数

class Complex{
    private:
        double real,imag;
    public:
        void Set(double r,double i);
        Complex(double r,double i);
        Complex(double r);
        Complex(Complex c1, Complex c2);

};
Complex::Complex(double r,double i){
    real=r;imag=i;
}
Complex::Complex(double r)
{
    real = r;imag = 0;

}
Complex::Complex(Complex c1,Complex c2)
{
    real = c1.real+c2.real;
    imag = c1.imag +c2.imag;
}

Complex c1(3),c2(1,0),c3(c1,c2);
//c1={3,0},c2={1,0},c3={4,0}

构造函数在数组中的使用

Class CSample{
    int x;
public:
        CSample(){
            cout<<"Constructor 1 Called"<<endl;
        }
        CSample(int n){
            x = n;
            cout<<"Constructor 2 Called"<<endl;
        }
};

int main(){
    CSample array1[2];
    cout<<"step1"<<endl;
    CSample array2[2] = {4,5};
    cout<<"step2"<<endl;
    CSample array3[2] ={3};
    cout<<"step3"<<endl;
    CSample *array4 = new CSample[2];
    delete []array4;
    return 0;
}

输出:
Constructor 1 Called
Consturctor 1 Called
step1
Constructor 2 Called
Constructor 2 Called
step2
Constructor 2 Called
Constructor 1 Called
step 3
Constructor 1 Called   
Constructor 1 Called  
class  
{
public://下面是三个构造函数
     Test(int n){}        //(1)
     Test(int n,int m){}  //(2)
     Test(){}             //(3)
};
Test array1[3] = {1,Test(1,2)};   //array1中的三个元素分别使用(1)(2)(3)初始化
Test * parray2[3] = {new Test(4),new Test(1,2)};  //new的返回值是地址,这条语句只生成了两个对象
//而不是三个,分别用了(1)(2),因为parray[2]我们没有初始化它,这个仅仅只是一个指针,而且不知道指向哪里

复制构造函数

  • 只有一个参数,即对同类对象的引用
  • 形如 X::X(X&) 或者 X::X(const X &) , 二者选一,后者能以常量对象作为参数
  • 如果没有定义复制构造函数,那么编译器生成默认复制构造函数,默认的复制构造函数完成复制功能
class Complex{
    private:
            double real,imag;
};
Complex c1;         //调用缺省无参构造函数
Complex c2(c1);     //调用缺省的复制构造函数,将c2初始化成和c1一样

一个类只有一个复制构造函数,要么是你自己写的,要么是编译器帮你生成的

class Complex{
    private:
            double real,imag;
    //自己写了一个无参构造函数
    Complex(){}
    //自己写了一个复制构造函数
    Complex(const Complex & c){
        real = c.real;
        imag = c.imag;
        cout<<"copy Constructor called";
    }
};
Complex c1;
Complex c2(c1);

不允许有形如 X::X(X) 的构造函数

复制构造函数起作用的三种情况:

  • 当用一个对象去初始化同类的另一个对象时;
Complex c2(c1);
Complex c2 = c1;  //和上面那一条语句是等价的,但是是初始化语句,不是赋值语句
  • 如果某个函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用
class A
{
    public:
    A(){};
    A(A & a){
        cout<<"copy constructor called"<<endl;
    }
};
C++规定,如果赋值函数的参数是对象的话,使用复制构造函数赋值
void Func(A a1){}

int main()
{
    A a2;
    Func(a2);
    return 0;
}
  • 如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用
class A
{
    public:
    int v;
    A(int n){v=n};
    A(const A & a){
        v = a.v;
        cout<<"copy constructor called"<<endl;
    }
};

A Func(){
    A b(4);
    return b;
}
int main(){
        cout<<Func.v<<endl;
        return 0;
}

对象间的赋值并不导致赋值构造函数被调用

类型转换构造函数

  • 定义转换构造函数的目的是实现类型的自动转换
  • 只有一个参数,而且不是复制构造函数的构造函数,一般就可以看做是转换构造函数
  • 当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量)
class Complex{
    public:
        double real,imag;
        Complex(int i){//类型转换构造函数,
            cout<<"IntConstructor called"<<endl;
            real = i;imag = 0;
        }
        Complex(double r,double i){real = r;imag=i;}
};;
int main()
{
    Complex c1(7,8);
    Complex c2=12;
    c1=9; //9被自动转换成一个临时Complex对象
    cout<<c1.real<<","<<c1.imag<<endl;
    return 0;
}

析构函数

  • 名字与类名相同,在前面加 ‘~’ ,没有参数和返回值,一个类最多只能有一个析构函数
  • 析构函数对象消亡时即被自动调用,可以定义析构函数在对象消亡前做善后工作,比如释放分配的空间等
  • 如果定义类时没写析构函数,那么编译器生成缺省析构函数,缺省析构函数什么也不做
  • 如果定义了析构函数,编译器不生成析构函数
class String{
    private:
        char *p;
    public:
        String(){
            p = new char[10];
        }
        ~ String();

};
String :: ~String()
{
    delete []p;
}

析构函数和数组
对象数组的生命期结束后,对象数组的每个元素的析构函数都会被调用

class Ctest{
    public:
        ~Ctest(){cout<<"destructor called"<<endl;}
};
int main()
{
    Ctest array[2];
    cout<<"End main"<<endl;
    return 0;
}
main结束时array中的对象会消亡,调用析构函数

delete 函数导致析构函数的消亡

class Ctest{
    public:
        ~Ctest(){cout<<"destructor called"<<endl;}
};
Ctest * pTest;
--------------------------------
pTest = new Ctest;  
delete pTest;
--------------------------------

pTest = new Ctest[3];   //构造函数调用三次
delete []pTest;         //析构函数调用三次

析构函数在对象作为函数返回值返回后被调用

class CMyclass{
    public:
    ~CMyclass(){cout<<"destructor"<<endl;}
};
CMyclass obj;
CMyclass fun(CMyclass sobj){ //参数对象消亡也会导致析构函数调用
    return sobj;      //函数调用返回时生成临时对象返回
}                     
int main()
{
    obj = fun(obj);  //  函数对象调用的返回值(临时对象)被调用过后,该临时对象析构函数被调用
    return 0;        
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新城里的旧少年^_^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值