c++自动提供下一继承成员函数:
● 默认构造函数,如果没有定义构造函数。
● 默认析构函数
● 复制构造函数
● 赋值运算符
● 地址运算符
默认构造函数
如果没有提供任何构造函数,将创建默认构造函数。
例如:
Klunk::Klunk() {}
实际上编译器生成一个不接收任何参数,也不执行任何操作的构造函数,c++创建对象总是会调用一个构造函数的。
如果定义了某个构造函数,将不会定义默认构造函数,如果要使用默认构造函数,必须重写一个默认构造函数。
带参数的构造函数也可能是默认构造函数,例如以下情况:
Klunk::Klunk(m = 1, n = 2) {
...
}
Klunk::Klunk(m = 1) {
...
}
所有参数都有默认值的话,也属于默认构造函数,为了避免二义性,默认构造函数只能有一个
//使用默认构造函数的几种方式。
Klunk lunk;
Klunk lunk = lunk();
Klunk *lunk = new lunk();
复制构造函数
复制构造函数用于将一个对象复制到新创建的对象中;就是说用于初始化过程,而不是赋值过程。
原型: class_name(const Class_name &);
MyString(const MyString &);
何时调用复制构造函数
1 新建对象,并初始化为已有的对象。
MyString str1; //调用默认构造函数
MyString myStr(str1); //使用复制构造函数
MyString myStr = str1; //使用复制构造函数
MyString *myStr = new MyString(str1); //使用复制构造函数
当函数安置传递对象,或函数返回对象时,都将使用复制构造函数。
所以按值传递对象会增加存储空间以及调用构造函数的时间开销
默认的复制构造函数
默认的复制构造函数逐个复制非静态成员(成员复制又叫浅复制),复制的是成员的值。
MyString myStr = str1;将str1的成员变量逐个复制给myStr,如果成员有指针,只会复制指针的值。
所以如果成员变量是一个指针的话,需要深拷贝指针所指向的内容。如果只是复制指针的值的话,两个对象的指针将指向同一快内存,当其中一个对象delete的时候,另一个在delete将出现重复删除,引起crash。
可以像下边一样重写一个复制构造函数,创建一块内存,来存储。
MyString::MyString(const MyString & st) {
len = st.len;
str = new char[len +1];
std::strcpy(str, st.str);
}
赋值运算符
赋值运算符的使用场景是:将已有的对象赋值给另外一个对象,这是后调用赋值运算符,注意和复制构造函数的区别,复制构造函数是用在创建对象的初始化阶段。
例如:
MyString string1;
string1 = string2;//string2是已有的对象。
赋值运算符是通过自动重载赋值运算符实现的。
原型:Class_name & Class_name::operator=(const Class_name &); 参数和返回值是一个类对象引用
赋值运算符和复制构造函数一样,都是浅拷贝。如果有指针存在,需要重写。
使用指向对象的指针
MyString *myStr = new MyString(str1);
使用new 来创建对象,并用myStr指向此对象,此对象将存储在堆内存中,当对象使用完之后,必须用delete删除对象。
何时调用析构函数:
1 如果对象是动态变量,则当执行完定义该对象的程序块时,将调用对象的析构函数。
2 如果对象时静态变量(外部全局变量、静态对象),则程序结束时候调用析构函数。
3 如果时new创建的,当调用delete时候,调用析构函数。
对于构造函数使用new的类,应该注意以下几点:
1 对于指向的内存时由new分配的所有类成员,有应在类的析构函数中对其使用delete,delete将释放分配的内存
2 如果析构函数通过对指针类成员使用delete来释放内存,则每个构造函数都应该使用new来初始化指针,或设置为空指针。
3 构造函数中要么使用new,要么使用new[],不能混用,因为析构函数只有一个,new[]对应delete[];
4 应定义一个重载复制运算符的类成员函数,如下所示
c_name & c_name::operator=(const c_name & cn) {
if (this == &cn) {
retunr *this;
}
delete[] c_pointor;
c_pointor = new typr_name[size];
return *this;
}
例子:
#ifndef __MY_STRING_H__
#define __MY_STRING_H__
void start_slt_eng();
class MyString {
private:
char * _str;
int _len;
static int _num_string;
public:
MyString();
MyString(const char * str);
MyString(const MyString & str);
friend std::ostream & operator<<(std::ostream & os, const MyString & str);
const MyString & operator=( const MyString & str);
~MyString();
};
#endif //__MY_STRING_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include "my_string.h"
using namespace std;
void start_my_string_text() {
MyString str = MyString();
MyString str1("textmy string");
MyString *str2 = new MyString("textmy p string");
cout << str1 << endl;
cout << str2 << endl;
cout << *str2 << endl;
MyString str3(str1);
delete str2;
MyString str4;
str4 = str1;
cout << "fuzhi" << str4;
}
MyString::MyString() {
_str = new char[12];
memcpy(_str , {"Default str"}, 12);
_len = strlen(_str);
cout << "Default constructor: " << _str<< endl;
}
MyString::MyString(const char * str) {
_len = strlen(str);
_str = new char[_len];
memcpy(_str, str, _len);
cout << "constructor: " << _str<< endl;
}
MyString::MyString(const MyString & str) {
_len = str._len;
_str = new char[_len];
memcpy(_str, str._str, _len);
cout << "Copy constructor: " << _str<< endl;
}
ostream & operator<<(ostream & os, const MyString & str) {
os << str._str << endl;
return os;
}
const MyString & MyString::operator=( const MyString & str) {
_len = str._len;
_str = new char[_len];
memcpy(_str, str._str, _len);
cout << "= constructor: " << _str << endl;
return *this;
}
MyString::~MyString() {
cout << "delete: " << _str << endl;
delete [] _str;
}