Наследование с целью использования Private конструктора

628
04 января 2017, 03:03

Привет! Я питонист, но из приличия решил заботать с++. Задание: есть класс Foo с private конструктором. Есть функция foo_says которая использует public метод класса Foo. Надо написать функцию get_foo, благодаря которой будет компилиться:

foo_says(get_foo("Hello!"));

Код:

#include <iostream>
#include <stdio.h>
#include <cstddef> // size_t
#include <cstring> // strlen, strcpy
using namespace std;

struct Foo {
    void say() const { std::cout << "Foo says: " << msg << "\n"; }
protected:
     Foo(const char *msg) : msg(msg) { }
private:
     const char *msg;
};
struct Fo : Foo {
    Fo(const char* msg) : Foo(msg) { }
};
void foo_says(const Foo &foo) {
    foo.say(); }
const Foo& get_foo(const char *msg) {
    const Fo F(msg);
    const Foo& f = F;
    return f;;
}
int main()
{
    foo_says(get_foo("Hello!"));
    return 0;
}

Решение и проблема: Так как задание из раздела "Наследование классов", то я наследовал Fo, который создаст объект, а потом его можно будет привести(срезкой) к объекту Foo. Но вместо строки Hello я получаю ��.��. Подозреваю, что после выхода из какой то функции удаляется указатель на Hello, потому что "Foo says" выводится исправно. Тогда где это происходит - понять не могу?

Answer 1

Начнем с того, что у вас же задано

класс Foo с private конструктором

а вы делаете protected. Чтобы достучаться до private конструктора, нужен только друг. Так что если позарез нужно наследование, то объявите наследник другом:

class Fo;
class Foo
{
    Foo(const char *msg) : msg(msg) { }
public:
    void say() const { std::cout << "Foo says: " << msg << "\n"; }
private:
    string msg;
    friend class Fo;
};
class Fo : public Foo {
public:
    Fo(const char* msg) : Foo(msg) { }
};

Что касается функции get_foo, то в ней можно выкрутиться (@Majestio рассказал вам, в чем в ней неприятности), либо возвращая указатель

const Foo* get_foo(const char *msg)
{
    return new Fo(msg);
}

либо ссылку на статический объект:

const Foo& get_foo(const char *msg)
{
    static Fo(msg);
    return Fo;
}

Минус второго решения очевиден - объект один, и вы не сможете создать второй. Минус первого - в возврате указателя, который надо хранить и не забыть потом удалить.

Главный проектный минус - что вы хотите возвращать ссылку на Foo, которая фактически является ссылкой на Fo.

Лично я бы решал задачу без наследования, ибо в условии о нем ни слова, а для доступа к private конструктору все равно нужна дружба:

class Foo
{
    Foo(const char *msg) : msg(msg) { }
public:
    void say() const { std::cout << "Foo says: " << msg << "\n"; }
private:
    string msg;
    friend Foo get_foo(const char *msg);
};
void foo_says(const Foo &foo)
{
    foo.say();
}
Foo get_foo(const char *msg)
{
    return Foo(msg);
}
int main()
{
    foo_says(get_foo("Hello!"));
}

Кстати, обратите внимание на то, что член msg я сделал string - сейчас это не принципиально, но вдруг вы еще как-то захотите использовать этот класс, и будете передавать в конструктор не литерал...

Answer 2
const Foo& get_foo(const char *msg) {
  const Fo F(msg);
  const Foo& f = F;
  return f;;
}

Вот тут вы возвращаете ссылку на объект локальной видимости. Это ведет к состоянию "неопределенного поведения" (undefined behavior). Пока это не решено - дальше "копать" смысла нет.

READ ALSO
Segmentation fault при отправке POST запроса

Segmentation fault при отправке POST запроса

Написал простенький класс для отправки post запросаПроблема в том что на строчке

588
Не работает проект на VS17

Не работает проект на VS17

Здравствуйте, скачал сегодня кандидат-версию Visual Studio 17 ProНу решил посмотреть что да как

609
Уменьшить длину кода [требует правки]

Уменьшить длину кода [требует правки]

На данный момент длина кода - 127 символов без пробелов, табуляция и знаков новой строкиЗадача уменьшить длину кода хотя бы до 125 символов, что...

523