C++基础三(构造函数,形参默认值,函数重载,单例模式,析构函数,内联函数,拷贝构造函数)

C++有六个默认函数,分别是:

1、默认构造函数;

2、默认拷贝构造函数;

3、默认析构函数;

4、赋值运算符;

5、取址运算符;

6、取址运算符const;

构造函数

构造函数(初始化类成员变量):

1、属于类的成员函数之一

2、构造函数没有返回类型

3、构造函数的函数名必须与类名相同

4、构造函数不允许手动调用(不能通过类对象调用)

5、构造函数在类对象创建时会被自动调用

6、如果没有显示声明构造函数,系统会生成一个默认的构造函数

7、如果显示声明了构造函数,那么默认构造函数将不会在被创建

8、构造函数不被设置为静态成员函数

9、构造函数的函数首部之前或函数首部之后不能用const修饰

10、new一个类指针也会触发构造函数的调用, malloc不会

11、构造函数通过函数重载的方式也可以拥有多个

12、构造函数也可以使用形参默认值

13、构造函数可以是private的

cpp 复制代码
#include <string.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

#if 0 // 构造函数

class Node
{
    public:
        //Node();
        Node(int n = 0, char* ptr = NULL, float score = 1.3f);
        // static Node(); // error
        // Node() const;  // error
        // const Node();  // error

    private:
        int m_num;
        char* m_ptr;
        float m_fscore;
};


// 类成员变量初始化方式2(常用这种方式)
// 通常普通类成员变量都需要在构造函数上进行初始化。
//Node::Node() 
//    : m_num(111),
//      m_ptr(NULL),
//      m_fscore(0.0) 
//{
//    // 类成员变量初始化方式1:
//    //m_num = 10;
//
//    cout << "construct: " << m_num << endl;
//}

Node::Node(int n, char* ptr, float score)
    : m_num(n),
      m_ptr(NULL),
      m_fscore(score)
{
    m_ptr = new char[100];

    strcpy(m_ptr, ptr);

    cout << m_num << " : " << m_ptr << " : " << m_fscore << endl;
}

int main(int argc,char *argv[])
{
    // 触发构造函数调用
    Node n;
    // n.Node(); // error


    Node n1(1, "st", 1.1);

    // 触发构造函数调用
    //Node* n1 = new Node;

    // 不会触发构造函数调用
    //Node* n2 = (Node*)malloc(sizeof(Node));

    return 0;
}
#endif

形参默认值

形参默认值:

1、给函数的形参一个默认的值。

2、如果我们在调用函数时指定实参的值,则形参的默认值不生效。

3、如果调用函数时没有给实参值,形参默认值才会生效。

4、形参默认值只需要在函数声明时写出来即可。

如果有函数声明的前提下, 在函数定义时写形参默认值会报错。

5、形参默认值只能出现在形参列表的最右边(后边)。

cpp 复制代码
#if 0 // 形参默认值

class Node
{
    public:
        // 形参默认值
        void fun(int x, int y, int num = 12, int z = 1);
        void display();

    private:
        int m_num;
};

void Node::fun(int x, int y, int num, int z)
{
    m_num = num;
}

void Node::display()
{
    cout << m_num << endl;
}

int main()
{
    Node n;
    n.fun(1, 2, 12345);
    n.display();

    n.fun(1, 2);
    n.display();

    return 0;
}

#endif

函数重载

函数重载:

1、在C++允许出现同名函数

(1) 形参个数不同

(2) 形参类型不同

(3) 形参顺序不同: 前提类型不同

(4) 函数的返回类型不影响

2、函数重载时,调用函数是在编译期间就确定了具体调用对象,

因为我们将函数重载称之为静态联编

3、重载函数调用时,不要出现二义性问题

cpp 复制代码
#if 0 // 函数重载
#include <stdio.h>

void fun(int x, float y)
{
    cout << "fun(int x, float y)" << endl;
}

void fun(int x, int y)
{
    cout << "void fun(int x, int y)" << endl;
}

void fun(int x, int y, int z = 0)
{
    cout << "void fun(int x, int y, int z = 0)" << endl;
}

void fun()
{
    cout << "fun()" << endl;
}

void fun(float x, int y)
{
    cout << "fun(float x, int y)" << endl;
}

int main()
{   
    fun(1.1f, 12);
    fun(12, 1.3f);

    fun(1, 2, 3);
    fun(1, 2); // error: 出现了二义性

    return 0;
}

#endif

单例模式

cpp 复制代码
#if 1 // 设计模式:单例模式

class Node
{
    public:
        static Node* getInstance();

    private:
        Node();

    private:
        static Node* m_ptr;
};

Node* Node::m_ptr = NULL;

Node::Node()
{
    cout << "construst" << endl;
}

Node* Node::getInstance()
{
    if (m_ptr == NULL)
    {
        m_ptr = new Node;
    }

    return m_ptr;
}

int main()
{
    //Node n; // error: 'Node::Node()' is private
    //Node* n1 = new Node; // error: 'Node::Node()' is private
    //Node* n2 = (Node*)malloc(sizeof(Node)); // 可以

    Node* n = Node::getInstance();
    cout << n << endl; 

    Node* n1 = Node::getInstance();
    cout << n1 << endl; 

    Node* n2 = Node::getInstance();
    cout << n2 << endl; 

    Node* n3 = Node::getInstance();
    cout << n3 << endl; 

    Node* n4 = Node::getInstance();
    cout << n4 << endl; 
    return 0;
}

析构函数

析构函数:

1、类对象或类指针销毁时析构函数会被自动调用

2、如果没有显示声明析构函数,则系统会生成一个默认的析构函数

3、如果显示声明了析构函数,则不会在生成默认的析构函数

4、析构函数没有返回类型,没有形参列表,且函数名与类名相同

5、析构函数只有一个,不能重载

6、析构函数不能设置为私有的

7、析构函数可以手动调用,但不允许

8、如果类对象的作用域相同,那么在销毁时析构函数的执行顺序与构造相反。

cpp 复制代码
#include <iostream>
using namespace std;

class Node
{
    public:
        Node();
        Node(int n);
        ~Node();
        //~Node(int num = 0); // error: destructors may not have parameters
   
        void fun();
        inline void fun1()
        {
            cout << "fun1" << endl;
        }

    private:
        int* m_ptr;
};

Node::Node()
    : m_ptr(NULL)
{
    if (m_ptr == NULL)
    {
        m_ptr = new int[100];
    }

    cout << "construct" << endl;
}

Node::Node(int n)
    : m_ptr(NULL)
{
    if (m_ptr == NULL)
    {
        m_ptr = new int[100];
    }

    *m_ptr = n;

    cout << *m_ptr << "construct"  << endl;
}

void Node::fun()
{
    Node n(8);
    cout << "fun" << endl;
}

Node::~Node()
{
    cout << *m_ptr << "destruct" << endl;

    if (m_ptr != NULL)
    {
        delete[] m_ptr;
        m_ptr = NULL;
    }
}

int main(int argc,char *argv[])
{
    Node n0(0), n2(2);

    n0.fun();
    
    Node n3(3), n4(4), n5(5);
    //Node* n1 = new Node;
    //delete n1;

    // n0.~Node(); // 可以但不允许

    cout << "aaa" << endl;

    return 0;
}

内联函数

内联函数:

1、在普通类成员函数声明之前添加 inline 关键字

2、内联函数通常函数定义和函数声明都写在类声明中

3、内联函数的函数体中不能出现复杂结构

4、函数函数体中的代码一般3行左右

5、通常被频繁调用的函数才需要设置为内联函数

6、内联函数是进行函数逻辑的替换,其不进行真正的函数调用,减少系统开销

7、系统会自动判断当前函数是否是内联函数,也会自动判别是否需要使用内联。

内联函数与c的宏函数有什么不同:

1、宏函数是在编译阶段处理,是纯字符串替换; 内联函数是运行阶段处理,是函数逻辑替换; 2、宏函数无法调试; 内 联函数可以调试;

3、宏函数没有返回值; 内联函数有返回值;

4、宏函数不能访问类成员; 内联函数可以访问类成员;

拷贝构造函数

拷贝构造函数:

1、拷贝构造函数属于构造函数的重载

2、拷贝构造函数,要求形参中有一个当前类的类对象的引用

3、如果没有显示声明拷贝构造函数,系统会生成一个默认的拷贝构造函数

4、如果显示声明了拷贝构造函数,那么默认拷贝构造函数将不会在被创建

5、当使用一个老对象去构造一个新对象时会调用拷贝构造函数

(1) 用一个老对象构造一个新对象,或者新对象赋给老对象。

(2) 当函数的形参是一个非引用的类对象时,实参到形参传递时。

(3) 函数返回一个非引用的类对象时。

6、如果没有显示声明和定义拷贝构造函数时,类中的非指针类成员的值是可以被拷贝的。

默认拷贝构造函数负责执行了值的拷贝。

7、如果显示声明和定义拷贝构造函数后,类中的所有成员变量都需要我们手动进行拷贝。

8、拷贝分为浅拷贝和深拷贝,默认拷贝构造函数只能进行浅拷贝。

9、浅拷贝只进行值的拷贝,深拷贝还会执行内存拷贝。

cpp 复制代码
#include <iostream>
using namespace std;


class Node
{
    public:
        Node();
        Node(const Node& n);
        ~Node();

    private:
        int m_num;
        int* m_ptr;
};

Node::Node()
    : m_num(12),
      m_ptr(NULL)
{
    if (m_ptr == NULL)
    {
        m_ptr = new int[100];
    }

    *m_ptr = 123;

    cout << "construct" << m_num << " -- " << *m_ptr << endl;
}

Node::Node(const Node& n)
    : m_num(0)
{
    if (m_ptr == NULL)
    {
        m_ptr = new int[100];
    }

    *m_ptr = *(n.m_ptr);

    cout << "copy construct" << m_num << " -- " << *m_ptr << endl;
}

Node::~Node()
{
    cout << "destruct: " << m_num << " -- " << *m_ptr << " : " << m_ptr << endl;

    if (m_ptr != NULL)
    {
        delete[] m_ptr;
        m_ptr = NULL;
    }
}

int main(int argc,char *argv[])
{
    Node n1;
    Node n2(n1);

    return 0;
}
cpp 复制代码
#include <iostream>
using namespace std;


class Node
{
    public:
        Node();
        Node(const Node& n);
        ~Node();

        Node fun(Node n);

    private:
        int m_num;
};

Node::Node()
    : m_num(12)
{
    cout << "construct: " << m_num << endl;
}

Node::Node(const Node& n)
    : m_num(0)
{
    cout << "copy construct: " << m_num << endl;
}

Node Node::fun(Node n)
{
    n.m_num = 111;
    cout << "fun" << endl;

    return n;
}

Node::~Node()
{
    cout << "destruct: " << m_num << endl;
}

int main(int argc,char *argv[])
{
    Node n1;
    //Node n2(n1);
    //Node n3 = n1;
    
    //Node n3 = n1.fun(n1);
    
    // 如果有返回,但我们没有接收的时候,系统会自动生成一个临时(隐藏)对象
    // 当前的临时(隐藏)对象的生存周期是从return开始,当前函数调用语句执行完毕结束。
    n1.fun(n1);

    cout << "---------" << endl;

    return 0;
}
相关推荐
小蜗牛慢慢爬行1 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
新手小袁_J25 分钟前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
呆呆小雅26 分钟前
C#关键字volatile
java·redis·c#
Monly2127 分钟前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
小俊俊的博客29 分钟前
海康RGBD相机使用C++和Opencv采集图像记录
c++·opencv·海康·rgbd相机
Ttang2329 分钟前
Tomcat原理(6)——tomcat完整实现
java·tomcat
长风清留扬32 分钟前
小程序毕业设计-音乐播放器+源码(可播放)下载即用
javascript·小程序·毕业设计·课程设计·毕设·音乐播放器
钱多多_qdd40 分钟前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha42 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
_WndProc44 分钟前
C++ 日志输出
开发语言·c++·算法