【常见的C++面试题代码-未完待续】

实现一个string类

代码如下

#include <iostream>
#include <string.h>

using namespace std;

class myString{
public:
    //构造函数
    myString(const char* str = NULL);
    //拷贝构造函数
    myString(const myString& str);
    //赋值操作
    myString& operator=(const myString& str);
    ~myString();
    void show();

private:
    char* m_data;
};

myString::myString(const char* str) {
    if (str == NULL) {
        m_data = new char[1];
        if (m_data == nullptr) {
            cout << "memory allocate failed!" << endl;
            exit(1);
        }
        m_data[0] = '\0';
    } else {
        m_data = new char[strlen(str) + 1];
        if (m_data == nullptr) {
            cout << "memory allocate failed!" << endl;
            exit(1);
        }
        strcpy(m_data, str);
    }
}

myString::myString(const myString& str) {
    m_data = new char[strlen(str.m_data) + 1];
    if (m_data == nullptr) {
        cout << "memory allocate failed!" << endl;
        exit(1);
    }
    strcpy(m_data, str.m_data);
}

myString& myString::operator=(const myString& str) {
    if (this == &str) return *this;
    delete[] m_data;

    m_data = new char[strlen(str.m_data) + 1];
    if (m_data == nullptr) {
        cout << "memory allocate failed!" << endl;
        exit(1);
    }
    strcpy(m_data, str.m_data);
    return *this;
}

myString::~myString() {
    delete[] m_data;
}

void myString::show() {
    cout << m_data << endl;
}

int main() {
    myString a;
    myString c("asdf");
    myString b(c);
    a = c;
    a.show();
    b.show();
    c.show();
    return 0;
}

考察的点

对于类的构造函数,赋值拷贝函数的考察

为什么需要拷贝构造函数?

这里就涉及到一个深、浅拷贝的问题了。
首先来看下拷贝构造函数是干什么用的。

拷⻉构造函数的函数参数为对象本身的引⽤,⽤于根据⼀个已存在的对象复制 出⼀个新的该类的对象,⼀般在函数中会将已存在的对象的数据成员的值⼀⼀复制到新创建的 对象中。

如果没有自己定义拷⻉构造函数,则系统会默认创建⼀个拷⻉构造函数,但当类中有 指针成员时,最好不要使⽤编译器提供的默认的拷⻉构造函数,最好⾃⼰定义并且在函数中执 ⾏深拷⻉。

我们可以通过上面给出的例子来看

int main() {
    myString a;
    myString c("asdf");
    myString b(c);
    a = c;
    a.show();
    b.show();
    c.show();
    return 0;
}

在main函数中初始化了三个对象,其中a调用默认构造函数,c也是(自己调试一下就知道了),而b却是使用拷贝构造函数

如果在拷贝构造函数中并不是单纯的复制指针的值,而是重新分配一片内存。内存地址如下:
在这里插入图片描述
这里的b,c的m_data指向的是不同的内存地址并且都相差0x20 即32个bit即4个字节
这样的话在析构函数(如下):delete是不会出现问题的。内存是正常的释放的。

myString::~myString() {
    delete[] m_data;
}

但是如果不使用系统默认的拷贝构造函数的话:

在这里插入图片描述
这里就会发现b和c的m_data指向了同一片内存地址,这样的话在执行析构函数的时候就会对同一个地址delete两次。导致野指针的问题。

链表相关

单链表

企业一般不会考察静态链表

代码:

struct pNode
{
    int data;
    pNode *next;
    pNode(int _data):data(_data), next(NULL){}
    /* data */
}*head;

void addpHead(int val) {
    pNode *p = new pNode(val);
    p->next = head->next;
    head->next = p;
}
//删除p后面的节点,即p->left
void premove(pNode *p) {
    pNode *tmp = p->next;
    p->next = p->next->next;
    delete tmp;
}

双链表

struct tNode
{
    int key, val;
    tNode *left, *right;
    tNode(int _key, int _val):key(_key), val(_val), left(NULL), right(NULL) {}
    /* data */
}*L, *R;
//删除p节点,不需要知道之前的节点
void tremove(tNode *p) {
    p->right->left = p->left;
    p->left->right = p->right;
}
//在左节点插入
void Linsert(tNode *p) {
    p->right = L->right;
    p->left = L;
    L->right->left = p;
    L->right = p;
}
//在右端点插入
void Rinsert(tNode *p) {
    p->left = R->left;
    p->right = R;
    R->left->right = p;
    R->left = p;
}

智能指针

代码如下:

#include <iostream>
#include <string>
#include <memory>
using namespace std;
template <typename T>
class SharedPtr{
private:
    T *m_ptr_;        //内部指针,保证拷贝指向同一内存
    size_t *m_count_;

public:
//构造函数
    SharedPtr() : m_ptr_(nullptr), m_count_(new size_t) {}
    SharedPtr(T *p) : m_ptr_(p), m_count_(new size_t){
        if (p == nullptr) {
            m_count_ = 0;
        } else {
            m_count_ = 1;
        }
    }
//析构函数
    ~SharedPtr() {
        if (this->m_ptr_) {
            if (--(*m_count_) == 0) {
                delete m_count_;
                delete m_ptr_;
                m_count_ = nullptr;
                m_ptr_ = nullptr;
            }
        }
    }
    //拷贝赋值函数
    SharedPtr(const SharedPtr& ptr) {
        if (this != ptr) {
            this->m_ptr_ = ptr.m_ptr_;
            this->m_count_ = ptr.m_count_;
            (*this->m_count_) ++;
        }
    }
    //赋值拷贝重载=
    SharedPtr &operator=(const SharedPtr& ptr) {
        //防止自我赋值
        if (this->m_ptr_ == ptr.m_ptr_) {
            return *this;
        }
        if (this->m_ptr_) {
            if (--(*this->m_count_) == 0) {
                delete m_count_;
                delete m_ptr_;
                m_count_ = nullptr;
                m_ptr_ = nullptr;
            } 
        }
        m_ptr_ = ptr.m_ptr_;
        m_count_ = ptr.m_count_;
        (*m_count_)++;
        return *this;
    }
    //移动构造函数
    SharedPtr(SharedPtr&& ptr) {
        m_ptr_ = ptr.m_ptr_;
        m_count_ = ptr.m_count_;
        ++(*m_count_);
    }
    //移动赋值运算
    void operator=(SharedPtr&& ptr) {
        SharedPtr(std::move(ptr));
    }
    //重载->
    T& operator->() {
        if (this->m_ptr_) return this->m_ptr_;
    }
    //重载*
    T& operator*() {
        if (this->m_ptr_) return *(this->m_ptr_);
    }
    //返回用户个数
    size_t use_count() {
        return *(this->m_count_);
    }
};

void Std_shared_ptr_test() {
    shared_ptr<int>ptr(new int);
    shared_ptr<int>ptr2(ptr);
    shared_ptr<int> ptr3;
    ptr3 = ptr2;

    cout << "这是std::shared_ptr测试结果:" << endl;
	cout <<"ptr.use_count:"<<ptr.use_count() << endl;
	cout <<"ptr2.use_count:"<< ptr2.use_count() << endl;
	cout <<"ptr3.use_count:"<< ptr3.use_count() << endl;

    *ptr3 = 100;
    cout << "通过对*ptr3赋值 取ptr的值为:"<<*ptr << endl;
	cout << endl;

}

void Shared_ptr_Test() {
    SharedPtr<int>ptr(new int);
    SharedPtr<int>ptr2(ptr);
    SharedPtr<int>ptr3;
    ptr3 = ptr2;

    cout << "这是std::shared_ptr测试结果:" << endl;
	cout <<"ptr.use_count:"<<ptr.use_count() << endl;
	cout <<"ptr2.use_count:"<< ptr2.use_count() << endl;
	cout <<"ptr3.use_count:"<< ptr3.use_count() << endl;

    *ptr3 = 100;
    cout << "通过对*ptr3赋值 取ptr的值为:"<<*ptr << endl;
	cout << endl;
}

int main() {
    // std::string s = "hello world";
    // auto left = std::move(s);
    // std::cout << left << std::endl;
    // std::cout << s << std::endl;
    Shared_ptr_Test();
    Std_shared_ptr_test();
    return 0;
}

shared_ptr实现共享式拥有概念,多个智能指针可以指向相同对象,该对象和其相关资源会在最后一个引用被销毁时候释放

使用计数机制来表明资源被几个指针共享。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
java面试题真的很多,下面我来回答一个有关多线程的问题。 在Java中实现多线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。这两种方式有何区别? 继承Thread类的方式是直接定义一个类继承Thread,并重写它的run()方法。然后创建该类的对象,并调用对象的start()方法来启动线程。这种方式简单直接,但因为Java是单继承的,所以如果某个类已经继承了其他类,就不能再直接继承Thread类实现多线程。 实现Runnable接口的方式是定义一个类实现Runnable接口,并实现其唯一的抽象方法run()。然后创建Thread类的对象,将实现了Runnable的对象作为参数传递给Thread类的构造方法。最后调用Thread对象的start()方法来启动线程。这种方式灵活性更大,因为Java允许一个类实现多个接口,所以即使某个类已经继承了其他类,仍然可以通过实现Runnable接口来实现多线程。 另一个区别在于资源共享的问题。继承Thread类的方式,不管是数据还是方法,都是线程自己拥有的,不存在共享的情况。而实现Runnable接口的方式,多个线程可以共享同一个对象的数据和方法,因为多个线程共同操作的是同一个Runnable对象。 总结来说,继承Thread类方式简单直接,但只能通过单继承来实现多线程;实现Runnable接口方式更灵活,可以解决单继承的限制,并且多个线程可以共享同一个Runnable对象的数据和方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值