c++对象和类(1)-基础

2018-07-07 18:52:09

1.成员默认为 private (只有类内部才能访问),protected(内部和子类能访问),public都能访问
2.this指针指向对象本身,是对象的地址,this->height 代表对象本身的height,*this代表对象。
3.指向对象的指针访问成员时跟 struct 一样用->(因为对象是从结构体发展过来的,所以跟结构体有很多相似的地方)。
5.成员函数声明后面带 const 代表是常成员函数,不能修改对象内的任何成员,只能读取。
6.重载 = 多个同名的函数,但是参数不同,重写 = 子类重写覆盖父类的方法

//类声明 stock.h
#ifndef STOCK00_H_
#define STOCK00_H_

#include <string>

class Stock     // class declaration
{
    private:
        std::string company;
        long shares;
        double share_val;
        double total_val;
        void set_tot() { total_val = shares * share_val; }
    public:
        void acquire(const std::string & co, long n, double pr);
        void buy(long num, double price);
        void sell(long num, double price);
        void update(double price);
        void show();
};  // note semicolon at the end

#endif
//类实现 stock.cpp
#include <iostream>
#include "stock00.h"

void Stock::acquire(const std::string & co, long n, double pr)
{
    company = co;
    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        std::cout << "Number of shares purchased can't be negative. "
            << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative, "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more than you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();

    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show()
{
    std::cout << "Company: " << company
              << "  Shares: " << shares << '\n'
              << "  Share Price: $" << share_val
              << "  Total Worth: $" << total_val << '\n';
}


内联方法

定义位于类声明中的函数自动成为内联函数,因此,上面的 Stock::set_tot 为内联函数。类声明中常将短小的成员函数作为内联函数。

当然也可以在类声明外面定义内联函数。

class Stock
{
private:
...
void set_tot(); // definition kept separate
public:
...
};
inline void Stock::set_tot() // use inline in definition
{
    total_val = shares * share_val;
}


构造函数

构造函数在创建对象的时候调用。

Stock::Stock(const string & co, long n, double pr){
    company = co;
    if (n < 0){
        std::cerr << "Number of shares can’t be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;

    share_val = pr;
    set_tot();
}

c++ 提供了两种使用构造函数来初始化对象,构造函数不像成员函数可以被对象调用,只能用来创建对象。

//显式调用构造函数-更加紧凑
Stock freecls= Stock("World Cabbage", 250, 1.25);

//隐式调用构造函数
Stock freecls("Furry Mason", 50, 2.5);

默认构造函数

如果没有声明构造函数,编译器将会生成默认构造函数(该函数什么也不做),类似于如下。

Stock::Stock() { }

一旦声明了一个及以上的构造函数,那么编译器不再提供默认构造函数,如果需要,自己手动提供。

如上面提供一个非默认构造函数,那么下面的声明将出错。

Stock s1;

有两个办法来定义默认构造函数:

1.重载默认构造函数,接收0个参数

Stock::Stock(){}

2.对非默认构造函数提供默认值

Stock::Stock(const string & co = "Error", int n = 0, double pr = 0.0){}

下面是使用构造函数的例子

Stock first;   //隐式调用默认构造函数
Stock first = Stock();   //显式调用默认构造函数

// 隐式调用默认构造函数,利用new声明的必须要使用 delete 释放。
Stock *prelief = new Stock; 


Stock first("Concrete Conglomerate"); // 调用构造函数

#下面这句是声明函数,而非声明对象。
Stock second();

析构函数

用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用析构函数。析构函数完成清理工作,因此实际上很有用。例如,如果构造函数使用new来分配内存,则析构函数将使用 delete 来释放这些内存。 

Stock的构造函数没有使用new,因此析构函数实际上没有需要完成的任务。在这种情况下,只需让编译器生成一个什么要不做的隐式析构函数即可。

//析构函数定义
Stock::~Stock()     // verbose class destructor
{
    std::cout << "Bye, " << company << "!\n";
}

啥时候回调用析构函数呢

1.如果对象时动态变量,则执行完程序块时(是从函数返回)会调用。
2.如果对象是静态变量(外部、静态、静态外部或来自名名称弓箭),则在程序结束的时候会调用。
3.如果对象是new创建的,那么在显式调用 delete 删除对象时会调用

改进版例子

//stock.h
#ifndef STOCK10_H_
#define STOCK10_H_
#include <string>

class Stock
{
    private:
        std::string company;
        long shares;
        double share_val;
        double total_val;
        void set_tot() { total_val = shares * share_val; }
    public:
    // two constructors
        Stock();        // default constructor
        Stock(const std::string & co, long n = 0, double pr = 0.0);
        ~Stock();       // noisy destructor
        void buy(long num, double price);
        void sell(long num, double price);
        void update(double price);
        void show();
};

#endif
// stock.cpp
#include <iostream>
#include "stock10.h"

// constructors (verbose versions)
Stock::Stock()          // default constructor
{
    std::cout << "Default constructor called\n";
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const std::string & co, long n, double pr)
{
    std::cout << "Constructor using " << co << " called\n";
    company = co;

    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

Stock::~Stock()     // verbose class destructor
{
    std::cout << "Bye, " << company << "!\n";
}

void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        std::cout << "Number of shares purchased can't be negative. "
            << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more than you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show()
{
    using std::cout;
    using std::ios_base;
    // set format to #.###
    ios_base::fmtflags orig =
        cout.setf(ios_base::fixed, ios_base::floatfield);
    std::streamsize prec = cout.precision(3);

    cout << "Company: " << company
        <<"   Shares: " << shares << '\n';
    cout << "  Share Price: $" << share_val;
    // set format to #.##
    cout.precision(2);
    cout << "  Total Worth: $" << total_val << '\n';

    // restore original format
    cout.setf(orig, ios_base::floatfield);
    cout.precision(prec);
}

也可以声明对象数组

Stock obj_arr[4] = {
    Stock("freecls", 11.1,10),
    Stock(),
    Stock("沧浪水", 22, 33)
};

下面这样是行不通的,Months在没实例化对象时,没有开辟对 Months 的存储。

class Bakery
{
private:
const int Months = 12; // declare a constant? FAILS
double costs[Months];
...
//下面可以
enum {Months = 12};
double costs[Months];

利用静态变量声明也是可以的,因为静态变量不是存储在对象里。

static const int Months = 12;
double costs[Months];


复制构造函数

下面三种情况会调用默认的复制构造函数

1.返回对象将调用复制构造函数,返回引用则不会
2.一个对象以值传递的方式传入函数体调用
1.对象赋值也调用obj1 = obj2

显性的定义复制构造函数

Freecls::Freecls(const Freecls &st){

}
// version 1
Vector Max(const Vector & v1, const Vector & v2)
{
    if (v1.magval() > v2.magval())
        return v1;
    else
        return v2;
}
// version 2
const Vector & Max(const Vector & v1, const Vector & v2)
{
    if (v1.magval() > v2.magval())
        return v1;
    else
        return v2;
}

版本2效率更高,不会调用复制构造函数

类静态成员函数使用 类名::函数名 调用

#声明
static int HowMany(){return 1;}


//调用
int count = Freecls::HowMany();


c++11新枚举,作用域为类

下面的 class 也可以替换成 struct。

新枚举就不会造成命名冲突。

enum class egg {Small, Medium, Large, Jumbo};
enum class t_shirt {Small, Medium, Large, Xlarge};
t_shirt rolf = t_shirt::Large;


其构造函数使用 new 的类

如果类使用new运算符来分配类成员指向的内存,在设计时应采取一些预防措施(前面总结了这些预 防措施,应牢记这些规则,这是因为编译器并不知道这些规则,因此无法发现错误)。

1.对于指向的内存是由 new 分配的所有类成员,都应在类的析构函数中对其使用 delete,该运算符将释放分配的内存。
2.如果析构函数通过对指针类成员使用 delete 来释放内存,则每个构造函数都应当使用new来初始化指针,或将它设置为空指针。
3.构造函数中要么使用new[],要么使用new,而不能混用。如果构造函数使用的是new[],则析构函数应使用 delete[];如果构造函数使用的是new,则析构函数应使用 delete。
4.应定义一个分配内存(而不是将指针指向已有内存)的复制构造函数。这样程序将能够将类对象 初始化为另一个类对象。这种构造函数的原型通常如下

className(const className &)

5.应定义一个重载赋值运算符的类成员函数,其函数定义如下(其中 c_pointer是 c name的类成员, 类型为指向 type_ name的指针)。下面的示例假设使用new[来初始化变量c_ pointer:

c_name & c_name::operator=(const c_name & cn){
    if (this == & cn)
        return *this;
    delete [] c_pointer;

    c_pointer = new type_name[size];

    ...
    return *this;
}



©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。