类构造函数和动态内存分配

1. c++自动提供了以下的成员函数

默认构造函数,如果没有定义构造函数

默认析构函数,如果没有定义

复制构造函数,如果没有定义,java参见:https://blog.csdn.net/ShewMi/article/details/80788591

赋值运算符,如果没有定义

地址运算符,如果没有定义

移动构造函数

移动赋值运算符

a. 默认构造函数

编译器在没有提供构造函数时候,编译器将提供一个不接受任何参数,也不执行任何操作的构造函数

如果定义了构造函数,编译器将不提供任何默认构造函数,如果希望创建对象时不显示的对它进行初始化,则必须显示定义默认构造函数,这种构造函数没有任何参数,但是可以使用它来设置特定的值

b. 复制构造函数

复制构造函数用于将一个对象复制到一个新创建的对象中,也就是说,它用于初始化过程中,而不是常规的赋值过程中

调用的时机:

当函数的参数为类对象的时候

函数的返回值是类对象

对象需要另一个对象进行初始化

由于按值传递对象将调用赋值构造函数,因此应该使用按引用传递对象,这样可以节省调用构造函数的时间以及存储新对象的空间。

默认赋值构造函数的功能:逐个赋值非静态成员(成员赋值也称浅赋值),赋值的是成员的值,如果类中存在一个静态的对象计数变量,默认的复制构造函数不说明其行为,也不增加计数器的个数,但是析构函数会更新次数,解决方法是提供一个对计数进行更新的显示复制构造函数

 如果对象中存在指针变量,默认的复制构造函数将不会为该指针分配空间,复制时候会将原对象和复制对象的指针指向同一个位置,析构两个对象会对同一个指针delete两次,从而会出现严重的问题。

警告:如果类中包含了使用new 初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这称为深度复制。复制的另一种形式(成员复制或者浅复制)只是复制指针值,浅复制仅浅浅复制指针的信息,而不会深入挖掘以复制指针引用的结构

赋值构造函数本质是构造函数,所以可以按照下面方式显式调用:

c. 赋值运算符:

String knot;

String metoo = knot;  // 调用的拷贝构造函数

String youtoo;

youtoo = knot;  // 调用赋值运算符重载

默认的赋值运算符隐式实现也是对成员进行逐个赋值,如果成员本身就是类对象,则程序将使用这个类定义的赋值运算符来复制这个成员,静态数据成员不受影响。

注意:由于目标对象可能以及引用了以前分配的数据,所以函数应该使用delete[] 来释放这些数据

函数应当避免将对象赋值给自身,否则,在给对象重新赋值之前,释放内存操作可能会删除对象的内容。

函数返回一个指向调用对象的引用,通过返回一个对象,函数可以像赋值常规操作那样,连续进行赋值

d. 默认析构函数:

如果类中有使用new的指针成员变量,析构函数是必不可少的,此外,析构函数一般被定义为virtual,这是为了重载以后被重载的类也能够正常的释放内存

例:String类

#include 
using namespace std;
class String
{
private:
    char* str;
    int len;
    static int num_strings;
    static const int CINLIM = 80;    // static 修饰的常量在类中初始化
public:
    // constructors and other methods
    String();
    String(const char*);
    String(const String&);
    ~String();
    int length() const {
        return len; 
    }
    // overloaded operator methods
    String& operator=(const String &);
    String& operator=(const char *);
    char& operator[](int);
    const char& operator[](int) const;
    // overloaded operator friends
    friend bool operator<(const String&, const String&);
    friend bool operator>(const String&, const String&);
    friend bool operator==(const String&, const String&);
    friend ostream& operator<<(ostream&, const String&);
    friend istream& operator>>(istream&, String&);
    // static function
    static int howMany();
};
#include "String.h"
#include 
#include 
using namespace std;
#pragma warning(disable:4996)

int String::num_strings = 0;
int String::howMany() {
    return num_strings;
}

String::String(const char* s) {
    len = strlen(s);
    str = new char[len + 1];
    strcpy(str, s);
    num_strings++;
}

String::String() {
    len = 0;
    str = new char[1];
    str[0] = '\0';
    num_strings++;
}

String::String(const String& st) {
    num_strings++;
    len = st.len;
    str = new char[len + 1];
    strcpy(str, st.str);
}

String::~String() {
    --num_strings;
    delete[] str;
}

String& String::operator=(const String& st) {
    if (this == &st) {
        return *this;
    }
    delete[] str;
    len = st.len;
    str = new char[len + 1];
    strcpy(str, st.str);
    return *this;
}

String& String::operator=(const char* s) {
    delete[] str;
    len = strlen(s);
    str = new char[len + 1];
    strcpy(str, s);
    return *this;
}

char& String::operator[](int i) {
    return str[i];
}

const char& String::operator[](int i) const {
    return str[i];
}

bool operator<(const String& str1, const String& str2) {
    return strcmp(str1.str, str2.str);
}

bool operator>(const String& str1, const String& str2) {
    return str2 < str1;
}

bool operator==(const String& str1, const String& str2) {
    return strcmp(str1.str, str2.str) == 0;
}

ostream& operator<<(ostream& os, const String& st) {
    os << st.str;
    return os;
}

istream& operator>>(istream& is, String& st) {
    char temp[String::CINLIM];
    is.get(temp, String::CINLIM);
    if (is) st = temp;
    while (is && is.get() != '\n') {
        continue;
    }
    return is;
}

测试代码:

类构造函数和动态内存分配_第1张图片

 if(!cin || temp[0] == '0')

  break;

较早的get(char*, int)在 读取到空行后,返回的值不为false,然而,对于这些版本来说,如果读取了一个空行,则字符串的第一个字符将会是一个空字符

if(!cin ||  temp[0] == '\0')

  break;

如果执行了最新的c++标准,则if语句中的第一个条件将检测到空行,第二个条件用于旧版本实现检测空行。

2. 在构造函数中使用new时候应当注意的事项

a. 如果在构造函数中使用new来初始化指针成员,则应当在析构函数中使用delete

b. new 和delete必须互相兼容,new对应delete,new[] 对应于delete[]

c. 如果有多个构造函数,则必须以相同的方式使用New,要么都带括号,要么都不带,因为只有一个析构函数,所有的构造函数必须与它兼容,然而可以在一个构造函数中使用new初始化指针,另一个构造函数中将指针初始化为空,这是因为delete(无论是带中括号还是不带中括号)可以用于空指针。

NULL,0,还是nullptr:以前空指针可以用于0或NULL来表示,C程序员通常使用NULL而不是0,以指出这是一个指针,就像使用'\0'而不是0来表示空字符,以指出这是一个字符一样,然而C++传统上更喜欢使用简单的0,而不是等价的NULL,但C++11提供了关键字nullptr,这将是一种更好的选择。

d. 应当定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象。

e. 应当定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象。

3. 返回对象的说明

a. 返回指向const对象的引用

使用const引用的常见原因是旨在提高效率,对于何时可以采用这种方法存在一些限制,如果函数返回传递给它的对象,可以通过返回引用来提高效率。

需要说明:返回对象调用复制构造函数,而返回引用不用。引用指向的对象应该在调用函数执行时存在,传递的对象是const,这样返回的类型必须是const,这样才匹配。

b. 返回指向非const对象的引用

两种常见的返回非const对象情形:重载复制运算符以及重载与cout一起使用的<<运算符,operator=返回值用于连续赋值,operator<<用于拼接输出

c. 返回对象:

如果返回的对象是被调用函数中的局部变量。

d. 返回const对象

 4. 使用指向对象的指针

类构造函数和动态内存分配_第2张图片

 5. 队列模拟:

#include 
class Customer {
private:
    long arrive;
    int processtime;
public:
    Customer() {
        arrive = processtime = 0;
    }

    void set(long when) {
        processtime = rand() % 3 + 1;
        arrive = when;
    }

    long when() const{
        return arrive;
    }

    int ptime() const {
        return processtime;
    }
};
#include "Customer.h"
typedef Customer Item;
class Queue {
private:
    struct Node {
        Item item;
        Node* next;
    };
    enum {
        Q_SIZE = 10
    };
    Node* front;
    Node* rear;
    int items;    // current number of items in Queue
    const int qsize;    // maximum number of items in Queue
    // 防止系统产生默认的拷贝构造函数
    // 声明为私有后,调用Queue snick = nup; 以及 ss = nup系统将报错
    Queue(const Queue& q) : qsize(10) {}    // const修饰的非静态常量,必须使用初始化列表的方式初始化,同时也必须采用这种格式初始化引用数据成员
    Queue& operator=(const Queue& q) { return *this; }
public:
    Queue(int qs = Q_SIZE);
    ~Queue();
    bool isEmpty() const;
    bool isFull() const;
    int queueCount() const;
    bool enqueue(const Item& item);    // add item to end
    bool dequeue(Item& item);    // remove item from front
};
#include "Queue.h"

Queue::Queue(int qs) : qsize(qs) {    
    front = nullptr;
    rear = nullptr;
    items = 0;
}

Queue::~Queue() {
    Node* temp;
    while (front != nullptr) {
        temp = front;
        front = front->next;
        delete temp;
    }
}

int Queue::queueCount() const{
    return items;
}

bool Queue::isEmpty() const {
    return items == 0;
}

bool Queue::isFull() const {
    return items == qsize;
}

bool Queue::enqueue(const Item& item) {
    if (isFull()) {
        return false;
    }
    Node* add = new Node;
    add->item = item;
    add->next = NULL;
    items++;
    if (front == nullptr) {
        front = add;
    }
    else {
        rear->next = add;
    }
    rear = add;
    return true;
}

bool Queue::dequeue(Item& item) {
    if (front == nullptr) {
        return false;
    }
    item = front->item;
    items--;
    Node* temp = front;
    front = front->next;
    delete temp;
    if (items == 0) {
        rear = nullptr;
    }
    return true;
}
#include "Queue.h"
const int MIN_PER_HR = 60;
bool newcustomer(double x) {
    return (rand() * x / RAND_MAX) < 1;        // rand(),RADN_MAX在cstdlib文件中
}
int main() {
    srand(time(0));    // random initializing of rand()
    cout << "Case Study: Bank of Heather Automatic Teller\n";
    cout << "Enter maxinm size of queue:";
    int qs;
    cin >> qs;
    Queue line(qs);
    cout << "Enter the number of simulation hours: ";
    int hours;
    cin >> hours;
    long cyclelimit = MIN_PER_HR * hours;
    cout << "Enter the average of number of customer per hour: ";
    double perhour;
    cin >> perhour;
    double min_per_cust;
    min_per_cust = MIN_PER_HR;
    Item temp;
    long turnaways = 0;
    long customers = 0;
    long served = 0;
    long sum_line = 0;
    int wait_time = 0;
    long line_wait = 0;
    for (int cycle = 0; cycle < cyclelimit; cycle++) {
        if (newcustomer(min_per_cust)) {
            if (line.isFull()) {
                turnaways++;
            }
            else {
                customers++;
                temp.set(cycle);
                line.enqueue(temp);
            }
        }
        if (wait_time <= 0 && !line.isEmpty()) {
            line.dequeue(temp);
            wait_time = temp.ptime();
            line_wait += cycle - temp.when();
            served++;
        }
        if (wait_time > 0) {
            wait_time--;
        }
        sum_line += line.queueCount();
    }
    if (customers > 0) {
        cout << "Customers accepted: " << customers << endl;
        cout << " custormer served: " << served << endl;
        cout << " turnaways: " << turnaways << endl;
        cout << "average queue size: ";
        cout.precision(2);
        cout.setf(ios_base::fixed, ios_base::floatfield);
        cout << (double) sum_line / cyclelimit << endl;
        cout << " average wait time: " << (double)line_wait / served << " minutes\n";
    }
    else {
        cout << "No customers!\n";
    }
    cout << "Done!\n";
    return 0;

 

转载于:https://www.cnblogs.com/feng-ying/p/10533764.html

你可能感兴趣的:(类构造函数和动态内存分配)